PDO Prepared在单个查询中插入多行

时间:2009-07-24 08:11:03

标签: php pdo insert prepared-statement

我目前在MySQL上使用这种类型的SQL在一个查询中插入多行值:

INSERT INTO `tbl` (`key1`,`key2`) VALUES ('r1v1','r1v2'),('r2v1','r2v2'),...

关于PDO的读数,使用预准备语句应该比静态查询更好。

因此,我想知道是否可以使用预准备语句生成“通过使用一个查询插入多行值”。

如果是,我可以知道如何实施它?

23 个答案:

答案 0 :(得分:135)

使用PDO准备语句插入多个值

在一个execute语句中插入多个值。为什么,因为根据this page它比常规插入更快。

$datafields = array('fielda', 'fieldb', ... );

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

更多数据值,或者您可能有一个填充数据的循环。

使用准备好的插页,您需要知道要插入的字段以及要创建的字段数?占位符绑定您的参数。

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

基本上我们希望insert语句看起来像。

现在,代码:

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);
try {
    $stmt->execute($insert_values);
} catch (PDOException $e){
    echo $e->getMessage();
}
$pdo->commit();

虽然在我的测试中,使用多个插页和常规准备的单值插入时只有1秒的差异。

答案 1 :(得分:67)

与Balagtas先生一样的答案,稍微清楚一点......

最新版本MySQL和PHP PDO 执行支持多行INSERT语句。

SQL概述

SQL会看起来像这样,假设你想要INSERT的3列表。

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

ON DUPLICATE KEY UPDATE即使使用多行INSERT也能正常工作;追加这个:

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

PHP概述

您的PHP代码将遵循常规的$pdo->prepare($qry)$stmt->execute($params) PDO调用。

$params将是所有传递给INSERT的值的一维数组。

在上面的例子中,它应该包含9个元素; PDO将使用每组3作为单行值。 (插入3行,每列3列= 9个元素数组。)

实施

下面的代码是为了清晰起见,而不是效率。如果您愿意,可以使用PHP array_*()函数来更好地映射或遍历数据。您是否可以使用事务显然取决于您的MySQL表类型。

假设:

  • $tblName - 要插入
  • 的表的字符串名称
  • $colNames - 表的列名的一维数组               这些列名必须是有效的MySQL列标识符;如果它们不是
  • ,则用反引号(``)来逃避它们
  • $dataVals - mutli-dimensional数组,其中每个元素是INSERT的一行值的一维数组

示例代码

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

try {
   $stmt->execute($dataToInsert);
} catch (PDOException $e){
   echo $e->getMessage();
}

$pdo->commit();

答案 2 :(得分:38)

对于它的价值,我看到很多用户建议迭代INSERT语句,而不是像所选答案那样构建为单个字符串查询。我决定只用两个字段和一个非常基本的插入语句来运行一个简单的测试:

<?php
require('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>";
?>

虽然整个查询本身花费的时间为毫秒或更短,但后者(单个字符串)查询的速度始终快8倍或更多。如果这是为了反映在更多列上导入数千行,那么差异可能是巨大的。

答案 3 :(得分:33)

当数据数组很小时,Herbert Balagtas的接受答案很有效。对于较大的$ data数组,array_merge函数变得非常慢。我创建$ data数组的测试文件有28个cols,大约是80,000行。最终的剧本用 41s 来完成。

使用 array_push()创建$ insert_values而不是array_merge()会导致 100X加速,执行时间 0.41s

有问题的array_merge():

$insert_values = array();

foreach($data as $d){
 $question_marks[] = '('  . placeholders('?', sizeof($d)) . ')';
 $insert_values = array_merge($insert_values, array_values($d));
}

为了消除对array_merge()的需要,您可以构建以下两个数组:

//Note that these fields are empty, but the field count should match the fields in $datafields.
$data[] = array('','','','',... n ); 

//getting rid of array_merge()
array_push($insert_values, $value1, $value2, $value3 ... n ); 

然后可以按如下方式使用这些数组:

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

foreach($data as $d){
 $question_marks[] = '('  . placeholders('?', sizeof($d)) . ')';
}

$sql = "INSERT INTO table (" . implode(",", array_keys($datafield) ) . ") VALUES " . implode(',', $question_marks);

$stmt = $pdo->prepare ($sql);
try {
    $stmt->execute($insert_values);
} catch (PDOException $e){
    echo $e->getMessage();
}
$pdo->commit();

答案 4 :(得分:14)

两种可能的方法:

$stmt = $pdo->prepare('INSERT INTO foo VALUES(:v1_1, :v1_2, :v1_3),
    (:v2_1, :v2_2, :v2_3),
    (:v2_1, :v2_2, :v2_3)');
$stmt->bindValue(':v1_1', $data[0][0]);
$stmt->bindValue(':v1_2', $data[0][1]);
$stmt->bindValue(':v1_3', $data[0][2]);
// etc...
$stmt->execute();

或者:

$stmt = $pdo->prepare('INSERT INTO foo VALUES(:a, :b, :c)');
foreach($data as $item)
{
    $stmt->bindValue(':a', $item[0]);
    $stmt->bindValue(':b', $item[1]);
    $stmt->bindValue(':c', $item[2]);
    $stmt->execute();
}

如果所有行的数据都在一个数组中,我会使用第二个解决方案。

答案 5 :(得分:12)

这不是你使用预准备语句的方式。

每个查询插入一行是完全可以的,因为您可以使用不同的参数多次执行一个预准备语句。事实上,这是最大的优势之一,因为它允许您以高效,安全和舒适的方式插入大量行。

所以也许可以实现你提出的方案,至少对于固定数量的行,但几乎可以保证这不是你想要的。

答案 6 :(得分:8)

一个较短的答案:压缩按列排序的数据数组

//$array = array( '1','2','3','4','5', '1','2','3','4','5');
$arCount = count($array);
$rCount = ($arCount  ? $arCount - 1 : 0);
$criteria = sprintf("(?,?,?,?,?)%s", str_repeat(",(?,?,?,?,?)", $rCount));
$sql = "INSERT INTO table(c1,c2,c3,c4,c5) VALUES$criteria";

当插入1000个左右的记录时,你不需要遍历每个记录来插入它们,只要你需要的是值的计数。

答案 7 :(得分:5)

这是我的简单方法。

    $values = array();
    foreach($workouts_id as $value){
      $_value = "(".$value.",".$plan_id.")";
      array_push($values,$_value);
    }
    $values_ = implode(",",$values);

    $sql = "INSERT INTO plan_days(id,name) VALUES" . $values_."";
    $stmt = $this->conn->prepare($sql);
    $stmt->execute();

答案 8 :(得分:3)

这是我编写的一个类,使用purge选项进行多次插入:

<?php

/**
 * $pdo->beginTransaction();
 * $pmi = new PDOMultiLineInserter($pdo, "foo", array("a","b","c","e"), 10);
 * $pmi->insertRow($data);
 * ....
 * $pmi->insertRow($data);
 * $pmi->purgeRemainingInserts();
 * $pdo->commit();
 *
 */
class PDOMultiLineInserter {
    private $_purgeAtCount;
    private $_bigInsertQuery, $_singleInsertQuery;
    private $_currentlyInsertingRows  = array();
    private $_currentlyInsertingCount = 0;
    private $_numberOfFields;
    private $_error;
    private $_insertCount = 0;

    function __construct(\PDO $pdo, $tableName, $fieldsAsArray, $bigInsertCount = 100) {
        $this->_numberOfFields = count($fieldsAsArray);
        $insertIntoPortion = "INSERT INTO `$tableName` (`".implode("`,`", $fieldsAsArray)."`) VALUES";
        $questionMarks  = " (?".str_repeat(",?", $this->_numberOfFields - 1).")";

        $this->_purgeAtCount = $bigInsertCount;
        $this->_bigInsertQuery    = $pdo->prepare($insertIntoPortion.$questionMarks.str_repeat(", ".$questionMarks, $bigInsertCount - 1));
        $this->_singleInsertQuery = $pdo->prepare($insertIntoPortion.$questionMarks);
    }

    function insertRow($rowData) {
        // @todo Compare speed
        // $this->_currentlyInsertingRows = array_merge($this->_currentlyInsertingRows, $rowData);
        foreach($rowData as $v) array_push($this->_currentlyInsertingRows, $v);
        //
        if (++$this->_currentlyInsertingCount == $this->_purgeAtCount) {
            if ($this->_bigInsertQuery->execute($this->_currentlyInsertingRows) === FALSE) {
                $this->_error = "Failed to perform a multi-insert (after {$this->_insertCount} inserts), the following errors occurred:".implode('<br/>', $this->_bigInsertQuery->errorInfo());
                return false;
            }
            $this->_insertCount++;

            $this->_currentlyInsertingCount = 0;
            $this->_currentlyInsertingRows = array();
        }
        return true;
    }

    function purgeRemainingInserts() {
        while ($this->_currentlyInsertingCount > 0) {
            $singleInsertData = array();
            // @todo Compare speed - http://www.evardsson.com/blog/2010/02/05/comparing-php-array_shift-to-array_pop/
            // for ($i = 0; $i < $this->_numberOfFields; $i++) $singleInsertData[] = array_pop($this->_currentlyInsertingRows); array_reverse($singleInsertData);
            for ($i = 0; $i < $this->_numberOfFields; $i++) array_unshift($singleInsertData, array_pop($this->_currentlyInsertingRows));

            if ($this->_singleInsertQuery->execute($singleInsertData) === FALSE) {
                $this->_error = "Failed to perform a small-insert (whilst purging the remaining rows; the following errors occurred:".implode('<br/>', $this->_singleInsertQuery->errorInfo());
                return false;
            }
            $this->_currentlyInsertingCount--;
        }
    }

    public function getError() {
        return $this->_error;
    }
}

答案 9 :(得分:1)

这是此问题的另一种(苗条)解决方案:

首先,您需要使用count()对源数组的数据(此处为$ aData)进行计数。然后,使用array_fill()并生成一个新数组,该数组具有与源数组一样多的条目,每个条目的值为“(?,?)”(占位符的数量取决于您使用的字段;此处为2)。然后,需要对生成的数组进行内插,并且使用胶水作为逗号。 在foreach循环中,您需要针对所使用的占位符数量生成另一个索引(占位符数量*当前数组索引+ 1)。您需要在每个绑定值之后向生成的索引加1。

$do = $db->prepare("INSERT INTO table (id, name) VALUES ".implode(',', array_fill(0, count($aData), '(?,?)')));

foreach($aData as $iIndex => $aValues){
 $iRealIndex = 2 * $iIndex + 1;
 $do->bindValue($iRealIndex, $aValues['id'], PDO::PARAM_INT);
 $iRealIndex = $iRealIndex + 1;
 $do->bindValue($iRealIndex, $aValues['name'], PDO::PARAM_STR);
}

$do->execute();

答案 10 :(得分:1)

我就这样做了:

首先定义您将使用的列名,或将其留空,pdo将假设您要使用表中的所有列 - 在这种情况下,您需要按照它们出现的确切顺序通知行值在桌子上。

$cols = 'name', 'middleName', 'eMail';
$table = 'people';

现在,假设您已经准备好了二维数组。迭代它,并使用行值构造一个字符串,如下所示:

foreach ( $people as $person ) {
if(! $rowVals ) {
$rows = '(' . "'$name'" . ',' . "'$middleName'" . ',' .           "'$eMail'" . ')';
} else { $rowVals  = '(' . "'$name'" . ',' . "'$middleName'" . ',' . "'$eMail'" . ')';
}

现在,你刚才做的是检查是否已经定义了$ rows,如果没有,则创建它并存储行值和必要的SQL语法,这样它就是一个有效的语句。请注意,字符串应该放在双引号和单引号内,因此它们会被及时识别。

剩下要做的就是准备声明并执行,如下:

$stmt = $db->prepare ( "INSERT INTO $table $cols VALUES $rowVals" );
$stmt->execute ();

到目前为止,最多测试了2000行,执行时间很短。将进行更多的测试,如果我有进一步的贡献,将会回到这里。

问候。

答案 11 :(得分:1)

由于尚未提出建议,我非常确定LOAD DATA INFILE仍然是加载数据的最快方式,因为它禁用索引,插入所有数据,然后重新启用索引 - 所有这些都在一个请求中完成。

将数据保存为csv应该是相当简单的,记住fputcsv。 MyISAM是最快的,但你仍然可以在InnoDB中获得很高的性能。还有其他的缺点,但是如果要插入大量数据,我会选择这条路线,而不是在100行以下。

答案 12 :(得分:1)

虽然一个老问题所有的贡献对我帮助很大,所以这是我的解决方案,它在我自己的DbContext课程中有效。 $rows参数只是一个表示行或模型的关联数组数组:field name => insert value

如果你使用一个使用模型的模式,那么当将模型数据作为数组传递时,这很适合,比如模型类中的ToRowArray方法。

  

注意:不言而喻,但绝不允许传递参数   将此方法暴露给用户或依赖于已经过验证和清理的插入值以外的任何用户输入。 $tableName参数和列名应由调用逻辑定义;例如,User模型可以映射到用户表,其用户列表的列列表映射到模型的成员字段。

public function InsertRange($tableName, $rows)
{
    // Get column list
    $columnList = array_keys($rows[0]);
    $numColumns = count($columnList);
    $columnListString = implode(",", $columnList);

    // Generate pdo param placeholders
    $placeHolders = array();

    foreach($rows as $row)
    {
        $temp = array();

        for($i = 0; $i < count($row); $i++)
            $temp[] = "?";

        $placeHolders[] = "(" . implode(",", $temp) . ")";
    }

    $placeHolders = implode(",", $placeHolders);

    // Construct the query
    $sql = "insert into $tableName ($columnListString) values $placeHolders";
    $stmt = $this->pdo->prepare($sql);

    $j = 1;
    foreach($rows as $row)
    {
        for($i = 0; $i < $numColumns; $i++)
        {
            $stmt->bindParam($j, $row[$columnList[$i]]);
            $j++;
        }
    }

    $stmt->execute();
}

答案 13 :(得分:0)

这是怎么回事:

        if(count($types_of_values)>0){
         $uid = 1;
         $x = 0;
         $sql = "";
         $values = array();
          foreach($types_of_values as $k=>$v){
            $sql .= "(:id_$k,:kind_of_val_$k), ";
            $values[":id_$k"] = $uid;
            $values[":kind_of_val_$k"] = $v;
          }
         $sql = substr($sql,0,-2);
         $query = "INSERT INTO table (id,value_type) VALUES $sql";
         $res = $this->db->prepare($query);
         $res->execute($values);            
        }

其背后的想法是循环遍历数组值,为准备好的语句占位符的每个循环添加“ id号”,同时,将值添加到数组中以作为绑定参数。如果您不喜欢使用数组中的“键”索引,则可以在循环内添加$ i = 0和$ i ++。在此示例中,这两种方法都可以使用,即使您具有带有命名键的关联数组,只要键是唯一的,它仍然可以使用。只需做一点工作,嵌套数组也可以。.

**请注意,substr会将$ sql变量的最后一个空格和逗号去除,如果您没有空格,则需要将其更改为-1而不是-2。

答案 14 :(得分:0)

根据我的实验,我发现在单个事务中具有多个值行的mysql insert语句是最快的。

但是,如果数据过多,则mysql的max_allowed_packet设置可能会限制单个事务插入多个值行。因此,当数据大于mysql的max_allowed_packet大小时,以下功能将失败:

  1. singleTransactionInsertWithRollback
  2. singleTransactionInsertWithPlaceholders
  3. singleTransactionInsert

插入大数据方案中最成功的一种方法是transactionSpeed方法,但是它比上述方法消耗更多的时间。因此,要解决此问题,您可以将数据分成较小的块并多次调用单个事务插入,也可以使用transactionSpeed方法放弃执行速度。

这是我的研究

<?php

class SpeedTestClass
{
    private $data;

    private $pdo;

    public function __construct()
    {
        $this->data = [];
        $this->pdo = new \PDO('mysql:dbname=test_data', 'admin', 'admin');
        if (!$this->pdo) {
            die('Failed to connect to database');
        }
    }

    public function createData()
    {
        $prefix = 'test';
        $postfix = 'unicourt.com';
        $salutations = ['Mr.', 'Ms.', 'Dr.', 'Mrs.'];

        $csv[] = ['Salutation', 'First Name', 'Last Name', 'Email Address'];
        for ($i = 0; $i < 100000; ++$i) {
            $csv[] = [
                $salutations[$i % \count($salutations)],
                $prefix.$i,
                $prefix.$i,
                $prefix.$i.'@'.$postfix,
            ];
        }

        $this->data = $csv;
    }

    public function truncateTable()
    {
        $this->pdo->query('TRUNCATE TABLE `name`');
    }

    public function transactionSpeed()
    {
        $timer1 = microtime(true);
        $this->pdo->beginTransaction();
        $sql = 'INSERT INTO `name` (`first_name`, `last_name`) VALUES (:first_name, :last_name)';
        $sth = $this->pdo->prepare($sql);

        foreach (\array_slice($this->data, 1) as $values) {
            $sth->execute([
                ':first_name' => $values[1],
                ':last_name' => $values[2],
            ]);
        }

        // $timer2 = microtime(true);
        // echo 'Prepare Time: '.($timer2 - $timer1).PHP_EOL;
        // $timer3 = microtime(true);

        if (!$this->pdo->commit()) {
            echo "Commit failed\n";
        }
        $timer4 = microtime(true);
        // echo 'Commit Time: '.($timer4 - $timer3).PHP_EOL;

        return $timer4 - $timer1;
    }

    public function autoCommitSpeed()
    {
        $timer1 = microtime(true);
        $sql = 'INSERT INTO `name` (`first_name`, `last_name`) VALUES (:first_name, :last_name)';
        $sth = $this->pdo->prepare($sql);
        foreach (\array_slice($this->data, 1) as $values) {
            $sth->execute([
                ':first_name' => $values[1],
                ':last_name' => $values[2],
            ]);
        }
        $timer2 = microtime(true);

        return $timer2 - $timer1;
    }

    public function noBindAutoCommitSpeed()
    {
        $timer1 = microtime(true);

        foreach (\array_slice($this->data, 1) as $values) {
            $sth = $this->pdo->prepare("INSERT INTO `name` (`first_name`, `last_name`) VALUES ('{$values[1]}', '{$values[2]}')");
            $sth->execute();
        }
        $timer2 = microtime(true);

        return $timer2 - $timer1;
    }

    public function singleTransactionInsert()
    {
        $timer1 = microtime(true);
        foreach (\array_slice($this->data, 1) as $values) {
            $arr[] = "('{$values[1]}', '{$values[2]}')";
        }
        $sth = $this->pdo->prepare('INSERT INTO `name` (`first_name`, `last_name`) VALUES '.implode(', ', $arr));
        $sth->execute();
        $timer2 = microtime(true);

        return $timer2 - $timer1;
    }

    public function singleTransactionInsertWithPlaceholders()
    {
        $placeholders = [];
        $timer1 = microtime(true);
        $sql = 'INSERT INTO `name` (`first_name`, `last_name`) VALUES ';
        foreach (\array_slice($this->data, 1) as $values) {
            $placeholders[] = '(?, ?)';
            $arr[] = $values[1];
            $arr[] = $values[2];
        }
        $sql .= implode(', ', $placeholders);
        $sth = $this->pdo->prepare($sql);
        $sth->execute($arr);
        $timer2 = microtime(true);

        return $timer2 - $timer1;
    }

    public function singleTransactionInsertWithRollback()
    {
        $placeholders = [];
        $timer1 = microtime(true);
        $sql = 'INSERT INTO `name` (`first_name`, `last_name`) VALUES ';
        foreach (\array_slice($this->data, 1) as $values) {
            $placeholders[] = '(?, ?)';
            $arr[] = $values[1];
            $arr[] = $values[2];
        }
        $sql .= implode(', ', $placeholders);
        $this->pdo->beginTransaction();
        $sth = $this->pdo->prepare($sql);
        $sth->execute($arr);
        $this->pdo->commit();
        $timer2 = microtime(true);

        return $timer2 - $timer1;
    }
}

$s = new SpeedTestClass();
$s->createData();
$s->truncateTable();
echo "Time Spent for singleTransactionInsertWithRollback: {$s->singleTransactionInsertWithRollback()}".PHP_EOL;
$s->truncateTable();
echo "Time Spent for single Transaction Insert: {$s->singleTransactionInsert()}".PHP_EOL;
$s->truncateTable();
echo "Time Spent for single Transaction Insert With Placeholders: {$s->singleTransactionInsertWithPlaceholders()}".PHP_EOL;
$s->truncateTable();
echo "Time Spent for transaction: {$s->transactionSpeed()}".PHP_EOL;
$s->truncateTable();
echo "Time Spent for AutoCommit: {$s->noBindAutoCommitSpeed()}".PHP_EOL;
$s->truncateTable();
echo "Time Spent for autocommit with bind: {$s->autoCommitSpeed()}".PHP_EOL;
$s->truncateTable();

仅包含两列的表的100,000个条目的结果如下

$ php data.php
Time Spent for singleTransactionInsertWithRollback: 0.75147604942322
Time Spent for single Transaction Insert: 0.67445182800293
Time Spent for single Transaction Insert With Placeholders: 0.71131205558777
Time Spent for transaction: 8.0056409835815
Time Spent for AutoCommit: 35.4979159832
Time Spent for autocommit with bind: 33.303519010544

答案 15 :(得分:0)

我遇到了同样的问题,这就是我为自己完成的工作,并且为此我为自己创建了一个功能(如果有帮助,您可以使用它)。

示例:

插入国家(国家/地区,城市)值(德国,柏林),(法国,巴黎);

$arr1 = Array("Germany", "Berlin");
$arr2 = Array("France", "France");

insertMultipleData("countries", Array($arr1, $arr2));


// Inserting multiple data to the Database.
public function insertMultipleData($table, $multi_params){
    try{
        $db = $this->connect();

        $beforeParams = "";
        $paramsStr = "";
        $valuesStr = "";

        for ($i=0; $i < count($multi_params); $i++) { 

            foreach ($multi_params[$i] as $j => $value) {                   

                if ($i == 0) {
                    $beforeParams .=  " " . $j . ",";
                }

                $paramsStr .= " :"  . $j . "_" . $i .",";                                       
            }

            $paramsStr = substr_replace($paramsStr, "", -1);
            $valuesStr .=  "(" . $paramsStr . "),"; 
            $paramsStr = "";
        }


        $beforeParams = substr_replace($beforeParams, "", -1);
        $valuesStr = substr_replace($valuesStr, "", -1);


        $sql = "INSERT INTO " . $table . " (" . $beforeParams . ") VALUES " . $valuesStr . ";";

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


        for ($i=0; $i < count($multi_params); $i++) { 
            foreach ($multi_params[$i] as $j => &$value) {
                $stmt->bindParam(":" . $j . "_" . $i, $value);                                      
            }
        }

        $this->close($db);
        $stmt->execute();                       

        return true;

    }catch(PDOException $e){            
        return false;
    }

    return false;
}

// Making connection to the Database 
    public function connect(){
        $host = Constants::DB_HOST;
        $dbname = Constants::DB_NAME;
        $user = Constants::DB_USER;
        $pass = Constants::DB_PASS;

        $mysql_connect_str = 'mysql:host='. $host . ';dbname=' .$dbname;

        $dbConnection = new PDO($mysql_connect_str, $user, $pass);
        $dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

        return $dbConnection;
    }

    // Closing the connection
    public function close($db){
        $db = null;
    }

如果 insertMultipleData($ table,$ multi_params) 返回TRUE ,则您的数据已插入到数据库中。

答案 16 :(得分:0)

<强> test.php的

CHECKPOINT

<强> database.php中

<?php
require_once('Database.php');

$obj = new Database();
$table = "test";

$rows = array(
    array(
    'name' => 'balasubramani',
    'status' => 1
    ),
    array(
    'name' => 'balakumar',
    'status' => 1
    ),
    array(
    'name' => 'mani',
    'status' => 1
    )
);

var_dump($obj->insertMultiple($table,$rows));
?>

答案 17 :(得分:0)

我的真实世界示例,将所有德国邮政编码插入空表(稍后添加城镇名称):

// obtain column template
$stmt = $db->prepare('SHOW COLUMNS FROM towns');
$stmt->execute();
$columns = array_fill_keys(array_values($stmt->fetchAll(PDO::FETCH_COLUMN)), null);
// multiple INSERT
$postcode = '01000';// smallest german postcode
while ($postcode <= 99999) {// highest german postcode
    $values = array();
    while ($postcode <= 99999) {
        // reset row
        $row = $columns;
        // now fill our row with data
        $row['postcode'] = sprintf('%05d', $postcode);
        // build INSERT array
        foreach ($row as $value) {
            $values[] = $value;
        }
        $postcode++;
        // avoid memory kill
        if (!($postcode % 10000)) {
            break;
        }
    }
    // build query
    $count_columns = count($columns);
    $placeholder = ',(' . substr(str_repeat(',?', $count_columns), 1) . ')';//,(?,?,?)
    $placeholder_group = substr(str_repeat($placeholder, count($values) / $count_columns), 1);//(?,?,?),(?,?,?)...
    $into_columns = implode(',', array_keys($columns));//col1,col2,col3
    // this part is optional:
    $on_duplicate = array();
    foreach ($columns as $column => $row) {
        $on_duplicate[] = $column;
        $on_duplicate[] = $column;
    }
    $on_duplicate = ' ON DUPLICATE KEY UPDATE' . vsprintf(substr(str_repeat(', %s = VALUES(%s)', $count_columns), 1), $on_duplicate);
    // execute query
    $stmt = $db->prepare('INSERT INTO towns (' . $into_columns . ') VALUES' . $placeholder_group . $on_duplicate);//INSERT INTO towns (col1,col2,col3) VALUES(?,?,?),(?,?,?)... {ON DUPLICATE...}
    $stmt->execute($values);
}

您可以看到它完全灵活。您无需检查列数或检查列的位置。您只需要设置插入数据:

    $row['postcode'] = sprintf('%05d', $postcode);

我为一些查询字符串构造函数感到自豪,因为它们在没有像array_merge这样繁重的数组函数的情况下工作。特别是vsprintf()是一个很好的发现。

最后我需要添加2x while()以避免超出内存限制。这取决于你的内存限制,但它是避免问题的一个很好的通用解决方案(并且有10个查询仍然比10.000好得多)。

答案 18 :(得分:0)

以下是我的解决方案:https://github.com/sasha-ch/Aura.Sql 基于auraphp / Aura.Sql库。

用法示例:

$q = "insert into t2(id,name) values (?,?), ... on duplicate key update name=name"; 
$bind_values = [ [[1,'str1'],[2,'str2']] ];
$pdo->perform($q, $bind_values);

欢迎Bugreports。

答案 19 :(得分:0)

这对我有用

    $sql = 'INSERT INTO table(pk_pk1,pk_pk2,date,pk_3) VALUES '; 
    $qPart = array_fill(0, count($array), "(?, ?,UTC_TIMESTAMP(),?)");
 $sql .= implode(",", $qPart);
 $stmt =    DB::prepare('base', $sql);
     $i = 1;
     foreach ($array as $value) 
       { 
       $stmt->bindValue($i++, $value);
       $stmt->bindValue($i++, $pk_pk1);
       $stmt->bindValue($i++, $pk_pk2); 
      $stmt->bindValue($i++, $pk_pk3); 
      } 
    $stmt->execute();

答案 20 :(得分:0)

您可以使用此功能在单个查询中插入多行:

function insertMultiple($query,$rows) {
    if (count($rows)>0) {
        $args = array_fill(0, count($rows[0]), '?');

        $params = array();
        foreach($rows as $row)
        {
            $values[] = "(".implode(',', $args).")";
            foreach($row as $value)
            {
                $params[] = $value;
            }
        }

        $query = $query." VALUES ".implode(',', $values);
        $stmt = $PDO->prepare($query);
        $stmt->execute($params);
    }
}

$ row 是值数组的数组。 在您的情况下,您将使用

调用该函数
insertMultiple("INSERT INTO tbl (`key1`,`key2`)",array(array('r1v1','r1v2'),array('r2v1','r2v2')));

这样做的好处是可以使用预备语句,同时使用单个查询插入多行。安全!

答案 21 :(得分:-1)

数组联合应该比<div class="form-group"> <select class="selectTbx" id="combobox" placeholder="Zone" name="zone"> <option value="">Select one...</option> <%=this.strOptions %> </select> 更快,所以类似于:

array_push

答案 22 :(得分:-1)

此处提供的用于创建准备好的查询的大多数解决方案都需要更复杂。使用PHP的内置函数,您可以轻松地创建SQL语句,而不会产生大量开销。

给定$records,一个记录数组,其中每个记录本身就是一个索引数组(以field => value的形式),以下函数将把记录插入到给定的表$table中,在PDO连接$connection上,仅使用一个预准备语句。请注意,这是一个PHP 5.6+解决方案,因为在array_push的调用中使用了参数解包:

private function import(PDO $connection, $table, array $records)
{
    $fields = array_keys($records[0]);
    $placeHolders = substr(str_repeat(',?', count($fields)), 1);
    $values = [];
    foreach ($records as $record) {
        array_push($values, ...array_values($record));
    }

    $query = 'INSERT INTO ' . $table . ' (';
    $query .= implode(',', $fields);
    $query .= ') VALUES (';
    $query .= implode('),(', array_fill(0, count($records), $placeHolders));
    $query .= ')';

    $statement = $connection->prepare($query);
    $statement->execute($values);
}