【问题标题】:Insert multiple rows with PDO prepared statements使用 PDO 准备语句插入多行
【发布时间】:2013-11-09 22:08:47
【问题描述】:

我想知道是否可以使用一个准备好的语句插入多行。 以下是我通常如何将一行插入数据库的示例:

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

我要插入的值将来自一个数组,例如: $values[0]['val1']; $values[0]['val2']; $values[0]['val3']; $values[1]['val1']; $values[2]['val2'];

等等

这段代码可能必须一次插入几百行,我考虑过创建一个循环来创建数百个参数,然后为每行附加一个额外的插入的 sql 语句,但我认为必须有更好的方法。最好的方法是什么?

【问题讨论】:

  • 您目前对这个插入有什么问题吗?
  • 不,这个用于单个插入的代码工作正常,我想知道如何最好地以这种方式插入多行和占位符。
  • 从哪里获取要插入的值?
  • 只需遍历您的多个参数并执行。你试过了吗?
  • 实际上,仔细观察,我认为这是stackoverflow.com/questions/1176352/…的副本

标签: php mysql pdo prepared-statement


【解决方案1】:

首先要说的是,您可以插入多行,这要归功于一个 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...

一旦了解了这一点,您就可以使用 PDO(例如)获得良好的解决方案。
您必须记住,您需要一个完整的prepareexecute 进程(就安全性而言,您必须分别传递每个参数)。

假设您要插入的行结构如下:

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

您的目标是将此结果作为准备好的查询

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

与其对应的执行

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


好吧,你只有现在必须这样做:

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

然后……就是这样!

这样,每个参数都被单独处理,这就是您想要的(安全性、安全性、安全性!),并且所有这些都以动态方式进行,只有一个 INSERT 查询


如果要插入的行太多(请参阅this),则应逐一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);
}

【讨论】:

  • 我在这里遗漏了一些东西,或者那不起作用? "INSERT INTO Table (col1, col2, col3) VALUES (".implode(',', $args).")"; 将变为 "INSERT INTO Table (col1, col2, col3) VALUES (?,?,?,?,?,?)"; 两行,这是无效的? (?,?,?,?,?,?) 应该是 (?,?,?),(?,?,?)
  • @JCM 你是对的,我有点惊讶之前没有人提到它。现在已更正。谢谢你的评论!
  • @Edward 答案中有解释“太多”的参考链接
  • 啊抱歉我错过了,谢谢!无论如何,5 年后,你仍然支持这个答案,太棒了!
【解决方案2】:

如果您只插入几百行,我更喜欢像下面这样更简单的代码。准备一个单行 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 确实支持多行 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);

但是,如果您尝试创建一个包含与数据数组中的项目一样多的元组的单个 INSERT 语句,您可能会意外生成比 maximum packet length 更长的 SQL 语句。

如果您有数千行,以至于一次执行一行准备好的语句的开销太大,您应该使用LOAD DATA INFILE

【讨论】:

    【解决方案3】:

    为了保留你的代码,你必须创建一个循环来执行你需要的所有插入:

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

    但可以使用类似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); 
    

    【讨论】:

    • 实际上,最后一个答案没有使用绑定参数,所以不像我需要的那样安全。首先,准备发生在循环之外,因此不起作用。
    【解决方案4】:

    如果您的表是事务性的(例如 InnoDB),您可以使用 Transaction 来加快插入速度。事务还有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();
    

    【讨论】:

    • 这会导致单次插入查询,因此一次插入数千次仍然非常快?
    • @kentor 不,但是由于准备好的语句的性质,它仍然很快,当你触发更多的语句时,它会变得更有效率。
    猜你喜欢
    • 2016-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多