PDO Prepared Inserts multiple rows in single query PDO Prepared Inserts multiple rows in single query php php

PDO Prepared Inserts multiple rows in single query


Multiple Values Insert with PDO Prepared Statements

Inserting multiple values in one execute statement. Why because according to this page it is faster than regular inserts.

$datafields = array('fielda', 'fieldb', ... );$data[] = array('fielda' => 'value', 'fieldb' => 'value' ....);$data[] = array('fielda' => 'value', 'fieldb' => 'value' ....);

more data values or you probably have a loop that populates data.

With prepared inserts you need to know the fields you're inserting to, and the number of fields to create the ? placeholders to bind your parameters.

insert into table (fielda, fieldb, ... ) values (?,?...), (?,?...)....

That is basically how we want the insert statement to look like.

Now, the code:

function placeholders($text, $count=0, $separator=","){    $result = array();    if($count > 0){        for($x=0; $x<$count; $x++){            $result[] = $text;        }    }    return implode($separator, $result);}$pdo->beginTransaction(); // also helps speed up your inserts.$insert_values = array();foreach($data as $d){    $question_marks[] = '('  . placeholders('?', sizeof($d)) . ')';    $insert_values = array_merge($insert_values, array_values($d));}$sql = "INSERT INTO table (" . implode(",", $datafields ) . ") VALUES " .       implode(',', $question_marks);$stmt = $pdo->prepare ($sql);$stmt->execute($insert_values);$pdo->commit();

Although in my test, there was only a 1 sec difference when using multiple inserts and regular prepared inserts with single value.


Same answer as Mr. Balagtas, slightly clearer...

Recent versions MySQL and PHP PDO do support multi-row INSERT statements.

SQL Overview

The SQL will look something like this, assuming a 3-column table you'd like to INSERT to.

INSERT INTO tbl_name            (colA, colB, colC)     VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?) [,...]

ON DUPLICATE KEY UPDATE works as expected even with a multi-row INSERT; append this:

ON DUPLICATE KEY UPDATE colA = VALUES(colA), colB = VALUES(colB), colC = VALUES(colC)

PHP Overview

Your PHP code will follow the usual $pdo->prepare($qry) and $stmt->execute($params) PDO calls.

$params will be a 1-dimensional array of all the values to pass to the INSERT.

In the above example, it should contain 9 elements; PDO will use every set of 3 as a single row of values. (Inserting 3 rows of 3 columns each = 9 element array.)

Implementation

Below code is written for clarity, not efficiency. Work with the PHP array_*() functions for better ways to map or walk through your data if you'd like. Whether you can use transactions obviously depends on your MySQL table type.

Assuming:

  • $tblName - the string name of the table to INSERT to
  • $colNames - 1-dimensional array of the column names of the tableThese column names must be valid MySQL column identifiers; escape them with backticks (``) if they are not
  • $dataVals - mutli-dimensional array, where each element is a 1-d array of a row of values to INSERT

Sample Code

// setup data values for PDO// memory warning: this is creating a copy all of $dataVals$dataToInsert = array();foreach ($dataVals as $row => $data) {    foreach($data as $val) {        $dataToInsert[] = $val;    }}// (optional) setup the ON DUPLICATE column names$updateCols = array();foreach ($colNames as $curCol) {    $updateCols[] = $curCol . " = VALUES($curCol)";}$onDup = implode(', ', $updateCols);// setup the placeholders - a fancy way to make the long "(?, ?, ?)..." string$rowPlaces = '(' . implode(', ', array_fill(0, count($colNames), '?')) . ')';$allPlaces = implode(', ', array_fill(0, count($dataVals), $rowPlaces));$sql = "INSERT INTO $tblName (" . implode(', ', $colNames) .     ") VALUES " . $allPlaces . " ON DUPLICATE KEY UPDATE $onDup";// and then the PHP PDO boilerplate$stmt = $pdo->prepare ($sql);$stmt->execute($dataToInsert);$pdo->commit();


For what it is worth, I have seen a lot of users recommend iterating through INSERT statements instead of building out as a single string query as the selected answer did. I decided to run a simple test with just two fields and a very basic insert statement:

<?phprequire('conn.php');$fname = 'J';$lname = 'M';$time_start = microtime(true);$stmt = $db->prepare('INSERT INTO table (FirstName, LastName) VALUES (:fname, :lname)');for($i = 1; $i <= 10; $i++ )  {    $stmt->bindParam(':fname', $fname);    $stmt->bindParam(':lname', $lname);    $stmt->execute();    $fname .= 'O';    $lname .= 'A';}$time_end = microtime(true);$time = $time_end - $time_start;echo "Completed in ". $time ." seconds <hr>";$fname2 = 'J';$lname2 = 'M';$time_start2 = microtime(true);$qry = 'INSERT INTO table (FirstName, LastName) VALUES ';$qry .= "(?,?), ";$qry .= "(?,?), ";$qry .= "(?,?), ";$qry .= "(?,?), ";$qry .= "(?,?), ";$qry .= "(?,?), ";$qry .= "(?,?), ";$qry .= "(?,?), ";$qry .= "(?,?), ";$qry .= "(?,?)";$stmt2 = $db->prepare($qry);$values = array();for($j = 1; $j<=10; $j++) {    $values2 = array($fname2, $lname2);    $values = array_merge($values,$values2);    $fname2 .= 'O';    $lname2 .= 'A';}$stmt2->execute($values);$time_end2 = microtime(true);$time2 = $time_end2 - $time_start2;echo "Completed in ". $time2 ." seconds <hr>";?>

While the overall query itself took milliseconds or less, the latter (single string) query was consistently 8 times faster or more. If this was built out to say reflect an import of thousands of rows on many more columns, the difference could be enormous.