简体   繁体   English

使用PDO准备好的语句插入多行

[英]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] ['val1']; $values[0]['val2']; $ values [0] ['val2']; $values[0]['val3']; $ values [0] ['val3']; $values[1]['val1']; $ values [1] ['val1']; $values[2]['val2']; $ 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. 这段代码可能必须一次插入几百行,我考虑过创建一个循环以创建数百个参数,然后在sql语句后每行附加一个额外的插入,但是我认为必须有更好的方法。 What would be the best way to do this? 最好的方法是什么?

The first important thing to say is that you can insert multiple rows thanks to only one INSERT query 首先要说的是,由于只有一个 INSERT查询,因此您可以插入多行

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). 知道了这一点之后,便可以使用PDO获得良好的解决方案(例如)。
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). 您必须记住,您需要一个完整的prepareexecute过程(就安全性而言,您必须分别传递每个参数)。

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 这样,每个参数都可以单独处理,这就是您想要的(安全性,安全性,安全性!),并且只需一个INSERT查询就可以动态方式对所有参数进行处理


If you have too many rows to insert (see this ), you should execute one by one 如果要插入的行太多(请参阅 ),则应一个接一个地execute

$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);
}

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. 准备一个单行INSERT语句,然后遍历数据数组,对每行执行一次准备好的查询。

$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. MySQL当然支持多行INSERT语法,因此您可以尝试将它们组合在一起。

$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 . 但是,如果您尝试创建一个具有与数据数组中的项目一样多的元组的INSERT语句,则可能会意外地生成一个长度超过最大数据包长度的SQL语句。

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 . 如果您有成千上万的行,以至于一次只执行一行准备好的语句会产生太多开销,则应使用LOAD DATA INFILE

If your table is transactional (for example an InnoDB), you can use a Transaction to speed up your insertions. 如果您的表是事务性的(例如,InnoDB),则可以使用事务来加快插入速度。 A transaction has also the advantage of roll backs . 交易还具有回滚的优势。

$pdo = DB::getInstance();
$stmt = $pdo->prepare('INSERT INTO table VALUES (col1, col2, col3) VALUES (:val1, :val2, :val3)');

$pdo->beginTransaction();

// The queries are not executed yet, but pushed to a transaction "stack"
foreach ($values as $value) {
    $stmt->execute([
        ':val1' => $value['val1'],
        ':val2' => $value['val2'],
        ':val3' => $value['val3'],
    ]);
}

// Executes all the queries "at once"
$pdo->commit();

To keep your code, you have to make a loop for executing all insertions you need : 为了保留您的代码,您必须循环执行所需的所有插入操作:

$array_params = array();
$params[':val1']="val1 1";
$params[':val2']="val1 2";
$params[':val3']="val1 3";
$array_params[] = $params;

$params[':val1']="val2 1";
$params[':val2']="val2 2";
$params[':val3']="val2 3";
$array_params[] = $params;

$sql="INSERT INTO table (col1,col2,col3) VALUES (:val1,:val2,:val3)";
$stmt=DB::getInstance()->prepare($sql);
foreach($array_params as $params)
{
  $stmt->execute($params);
}

But its possible to execute multiple insertions with one query like INSERT INTO table (col1,col2,col3) VALUES ("val1","val2","val3"),("val4","val5","val6"),("val7","val8,"val9") , by using something like this to build the query: 但是可以用一个查询(例如INSERT INTO table (col1,col2,col3) VALUES ("val1","val2","val3"),("val4","val5","val6"),("val7","val8,"val9") ,通过类似以下方式来构建查询:

$all_inserts = array( array('val1', 'val2', 'val3'),array('val4', 'val5', 'val6'));
$sql = 'INSERT INTO table (col1,col2,col3) VALUES ';
$rows = array();
foreach ($all_inserts as $one_insert)
{
   $rows[] = '('.implode(',', $pdo->quote($one_insert).')';
}
$sql .= ' '.implode(',', $rows);
$pdo->query($sql); 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM