diff --git a/CHANGELOG.md b/CHANGELOG.md
index e92f048da..e1ef4bfa5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,7 @@
 - Bug #761: Quote aliases of CTE in `WITH` queries (@Tigrov)
 - Chg #765: Deprecate `SchemaInterface::TYPE_JSONB` (@Tigrov)
 - Enh #770: Move methods from concrete `Command` class to `AbstractPdoCommand` class (@Tigrov)
+- Bug #769, #61: Fix `AbstractDMLQueryBuilder::batchInsert()` for values as associative arrays (@Tigrov)
 
 ## 1.1.1 August 16, 2023
 
diff --git a/src/QueryBuilder/AbstractDMLQueryBuilder.php b/src/QueryBuilder/AbstractDMLQueryBuilder.php
index 1224e503f..5d57d6412 100644
--- a/src/QueryBuilder/AbstractDMLQueryBuilder.php
+++ b/src/QueryBuilder/AbstractDMLQueryBuilder.php
@@ -19,6 +19,7 @@
 
 use function array_combine;
 use function array_diff;
+use function array_fill_keys;
 use function array_filter;
 use function array_keys;
 use function array_map;
@@ -57,25 +58,31 @@ public function batchInsert(string $table, array $columns, iterable $rows, array
 
         $values = [];
         $columns = $this->getNormalizeColumnNames('', $columns);
+        $columnNames = array_values($columns);
+        $columnKeys = array_fill_keys($columnNames, false);
         $columnSchemas = $this->schema->getTableSchema($table)?->getColumns() ?? [];
 
         foreach ($rows as $row) {
             $i = 0;
-            $placeholders = [];
-
-            foreach ($row as $value) {
-                if (isset($columns[$i], $columnSchemas[$columns[$i]])) {
-                    $value = $columnSchemas[$columns[$i]]->dbTypecast($value);
+            $placeholders = $columnKeys;
+
+            foreach ($row as $key => $value) {
+                /** @psalm-suppress MixedArrayTypeCoercion */
+                $columnName = $columns[$key] ?? (isset($columnKeys[$key]) ? $key : $columnNames[$i] ?? $i);
+                /** @psalm-suppress MixedArrayTypeCoercion */
+                if (isset($columnSchemas[$columnName])) {
+                    $value = $columnSchemas[$columnName]->dbTypecast($value);
                 }
 
                 if ($value instanceof ExpressionInterface) {
-                    $placeholders[] = $this->queryBuilder->buildExpression($value, $params);
+                    $placeholders[$columnName] = $this->queryBuilder->buildExpression($value, $params);
                 } else {
-                    $placeholders[] = $this->queryBuilder->bindParam($value, $params);
+                    $placeholders[$columnName] = $this->queryBuilder->bindParam($value, $params);
                 }
 
                 ++$i;
             }
+
             $values[] = '(' . implode(', ', $placeholders) . ')';
         }
 
@@ -83,13 +90,13 @@ public function batchInsert(string $table, array $columns, iterable $rows, array
             return '';
         }
 
-        $columns = array_map(
+        $columnNames = array_map(
             [$this->quoter, 'quoteColumnName'],
-            $columns,
+            $columnNames,
         );
 
         return 'INSERT INTO ' . $this->quoter->quoteTableName($table)
-            . ' (' . implode(', ', $columns) . ') VALUES ' . implode(', ', $values);
+            . ' (' . implode(', ', $columnNames) . ') VALUES ' . implode(', ', $values);
     }
 
     public function delete(string $table, array|string $condition, array &$params): string
@@ -430,13 +437,11 @@ protected function normalizeColumnNames(string $table, array $columns): array
      */
     protected function getNormalizeColumnNames(string $table, array $columns): array
     {
-        $normalizedNames = [];
-
-        foreach ($columns as $name) {
-            $normalizedName = $this->quoter->ensureColumnName($name);
-            $normalizedNames[] = $this->quoter->unquoteSimpleColumnName($normalizedName);
+        foreach ($columns as &$name) {
+            $name = $this->quoter->ensureColumnName($name);
+            $name = $this->quoter->unquoteSimpleColumnName($name);
         }
 
-        return $normalizedNames;
+        return $columns;
     }
 }
diff --git a/tests/Provider/CommandProvider.php b/tests/Provider/CommandProvider.php
index 5d2ae6176..9f7408b73 100644
--- a/tests/Provider/CommandProvider.php
+++ b/tests/Provider/CommandProvider.php
@@ -339,7 +339,7 @@ public static function batchInsert(): array
                     ':qp3' => false,
                 ],
             ],
-            'with associative values' => [
+            'with associative values with different keys' => [
                 'type',
                 ['int_col', 'float_col', 'char_col', 'bool_col'],
                 'values' => [['int' => '1.0', 'float' => '2', 'char' => 10, 'bool' => 1]],
@@ -356,6 +356,74 @@ public static function batchInsert(): array
                     ':qp3' => true,
                 ],
             ],
+            'with associative values with different keys and columns with keys' => [
+                'type',
+                ['a' => 'int_col', 'b' => 'float_col', 'c' => 'char_col', 'd' => 'bool_col'],
+                'values' => [['int' => '1.0', 'float' => '2', 'char' => 10, 'bool' => 1]],
+                'expected' => DbHelper::replaceQuotes(
+                    <<<SQL
+                    INSERT INTO [[type]] ([[int_col]], [[float_col]], [[char_col]], [[bool_col]]) VALUES (:qp0, :qp1, :qp2, :qp3)
+                    SQL,
+                    static::$driverName,
+                ),
+                'expectedParams' => [
+                    ':qp0' => 1,
+                    ':qp1' => 2.0,
+                    ':qp2' => '10',
+                    ':qp3' => true,
+                ],
+            ],
+            'with associative values with keys of column names' => [
+                'type',
+                ['int_col', 'float_col', 'char_col', 'bool_col'],
+                'values' => [['bool_col' => 1, 'char_col' => 10, 'int_col' => '1.0', 'float_col' => '2']],
+                'expected' => DbHelper::replaceQuotes(
+                    <<<SQL
+                    INSERT INTO [[type]] ([[int_col]], [[float_col]], [[char_col]], [[bool_col]]) VALUES (:qp2, :qp3, :qp1, :qp0)
+                    SQL,
+                    static::$driverName,
+                ),
+                'expectedParams' => [
+                    ':qp0' => true,
+                    ':qp1' => '10',
+                    ':qp2' => 1,
+                    ':qp3' => 2.0,
+                ],
+            ],
+            'with associative values with keys of column keys' => [
+                'type',
+                ['int' => 'int_col', 'float' => 'float_col', 'char' => 'char_col', 'bool' => 'bool_col'],
+                'values' => [['bool' => 1, 'char' => 10, 'int' => '1.0', 'float' => '2']],
+                'expected' => DbHelper::replaceQuotes(
+                    <<<SQL
+                    INSERT INTO [[type]] ([[int_col]], [[float_col]], [[char_col]], [[bool_col]]) VALUES (:qp2, :qp3, :qp1, :qp0)
+                    SQL,
+                    static::$driverName,
+                ),
+                'expectedParams' => [
+                    ':qp0' => true,
+                    ':qp1' => '10',
+                    ':qp2' => 1,
+                    ':qp3' => 2.0,
+                ],
+            ],
+            'with shuffled indexes of values' => [
+                'type',
+                ['int_col', 'float_col', 'char_col', 'bool_col'],
+                'values' => [[3 => 1, 2 => 10, 0 => '1.0', 1 => '2']],
+                'expected' => DbHelper::replaceQuotes(
+                    <<<SQL
+                    INSERT INTO [[type]] ([[int_col]], [[float_col]], [[char_col]], [[bool_col]]) VALUES (:qp2, :qp3, :qp1, :qp0)
+                    SQL,
+                    static::$driverName,
+                ),
+                'expectedParams' => [
+                    ':qp0' => true,
+                    ':qp1' => '10',
+                    ':qp2' => 1,
+                    ':qp3' => 2.0,
+                ],
+            ],
         ];
     }