Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Insert multiple rows with PDO prepared statements

I would like to know if it is possible to insert multiple rows using one prepared statement. Below is an example of how I would normally insert one row into the db:

$params=array();
$params[':val1']="val1";
$params[':val2']="val2";
$params[':val3']="val3";
$sql="INSERT INTO table VALUES (col1,col2,col3) VALUES (:val1,:val2,:val3)";
$stmt=DB::getInstance()->prepare($sql);
$stmt->execute($params);

The values I want to insert will come from an array, for example: $values[0]['val1']; $values[0]['val2']; $values[0]['val3']; $values[1]['val1']; $values[2]['val2'];

etc.

This code may have to insert a few hundred rows at once, I thought about creating a loop to create hundreds of params and then append the sql statement with an extra insert for each row but I thought there must be a better way. What would be the best way to do this?

like image 611
ezero Avatar asked Oct 30 '13 11:10

ezero


People also ask

What are the advantages of PDO prepared statement?

Advantage of PDO PDO allows comparatively seamless switching between different databases and platforms, which can be easily done by changing the connection string. It does not support database-specific syntaxes. The PDO extension can access any database which is written for PDO driver.

What is the advantage of PDO over MySQL?

MySQLi procedural and MySQLi object-oriented only support MySQL database but PDO is an advanced method along with MySQL which supports Postgres, SQLite, Oracle, and MS SQL Server. PDO is more secure than the first two options and it is also faster in comparison with MySQLi procedural and MySQLi object-oriented.

What is PDO in php MySQL?

Introduction ¶ PDO_MYSQL is a driver that implements the PHP Data Objects (PDO) interface to enable access from PHP to MySQL databases. PDO_MYSQL uses emulated prepares by default. MySQL 8. When running a PHP version before 7.1. 16, or PHP 7.2 before 7.2.


2 Answers

The first important thing to say is that you can insert multiple rows thanks to only one INSERT query

INSERT INTO Table (col1, col2, col3) 
VALUES ('abc', 'def', 'ghi'),
       ('abc', 'def', 'ghi'),
       ('abc', 'def', 'ghi'),
       ('abc', 'def', 'ghi'),
       ('abc', 'def', 'ghi')
       -- and so on...

Once you know that, you're able to get a good solution with PDO (for instance).
You have to keep in mind that you want a complete prepare and execute process (in term of security, you have to pass each parameter separately).

Let's say you have rows to insert structured as follow:

$rows = array(
              array('abc', 'def', 'ghi'), // row 1 to insert
              array('abc', 'def', 'ghi'), // row 2 to insert
              array('abc', 'def', 'ghi')  // row 3 to insert
              // and so on ...
);

Your goal is to have this result as a prepared query:

INSERT INTO Table (col1, col2, col3) 
VALUES (?, ?, ?),
       (?, ?, ?),
       (?, ?, ?)

With its corresponding execute:

PDOStatement::execute(array('abc', 'def', 'ghi', 'abc', 'def', 'ghi', 'abc', 'def', 'ghi'));


Well, you only have to do it now:

$rows = array(
              array('abc', 'def', 'ghi'),
              array('abc', 'def', 'ghi'),
              array('abc', 'def', 'ghi')
);

$row_length = count($rows[0]);
$nb_rows = count($rows);
$length = $nb_rows * $row_length;

/* Fill in chunks with '?' and separate them by group of $row_length */
$args = implode(',', array_map(
                                function($el) { return '('.implode(',', $el).')'; },
                                array_chunk(array_fill(0, $length, '?'), $row_length)
                            ));

$params = array();
foreach($rows as $row)
{
   foreach($row as $value)
   {
      $params[] = $value;
   }
}

$query = "INSERT INTO Table (col1, col2, col3) VALUES ".$args;
$stmt = DB::getInstance()->prepare($query);
$stmt->execute($params);

And... That's it!

This way, each param is treated separately, which is what you want (security, security, security!) and all of it, in a dynamic way, with only one INSERT query


If you have too many rows to insert (see this), you should execute one by one

$rows = array(
              array('abc', 'def', 'ghi'), // row 1 to insert
              array('abc', 'def', 'ghi'), // row 2 to insert
              array('abc', 'def', 'ghi')  // row 3 to insert
              // and so on ...
);

$args = array_fill(0, count($rows[0]), '?');

$query = "INSERT INTO Table (col1, col2, col3) VALUES (".implode(',', $args).")";
$stmt = $pdo->prepare($query);

foreach ($rows as $row) 
{
   $stmt->execute($row);
}
like image 50
Justin Iurman Avatar answered Oct 01 '22 07:10

Justin Iurman


If you're only inserting a few hundred rows, I'd favor simpler code like the following. Prepare a single-row INSERT statement, and then loop over your data array, executing the prepared query once for each row.

$rows = array(
              array('abc', 'def', 'ghi'), // row 1 to insert
              array('abc', 'def', 'ghi'), // row 2 to insert
              array('abc', 'def', 'ghi')  // row 3 to insert
              // and so on ...
);

$params = implode(",", array_fill(0, count($rows[0]), "?"));

$sql = "INSERT INTO mytable VALUES ($params)";

$stmt = $pdo->prepare($sql); // rely on exceptions for error detection

foreach ($rows as $row) {
    $stmt->execute($row);
}

MySQL does support multi-row INSERT syntax of course, so you could try putting that together.

$params = implode(",", array_fill(0, count($rows[0]), "?"));

$tuples = "(" . implode("),(", array_fill(0, count($rows), $params)) . ")";

$sql = "INSERT INTO mytable VALUES $tuples";

$values = call_user_func_array("array_merge", $rows);

$stmt = $pdo->prepare($sql);

$stmt->execute($values);

But if you try to create a single INSERT statement with as many tuples as the items in your data array, you might accidentally generate an SQL statement that is longer than the maximum packet length.

If you have thousands of rows, enough so that executing a prepared statement one row at a time is too much overhead, you should use LOAD DATA INFILE.

like image 40
Bill Karwin Avatar answered Oct 01 '22 07:10

Bill Karwin