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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PDO Prepared在单个查询中插入多行相关的知识,希望对你有一定的参考价值。

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

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

在PDO上的读数中,使用预处理语句应该比静态查询具有更好的安全性。

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

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

答案

使用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秒的差异。

另一答案

这就是我做的方式:

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

<?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;
    }
}

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

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

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

剩下要做的就是准备语句并执行,如下:

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

到目前为止测试了多达2000行,执行时间很短。将运行更多的测试,并将返回到这里,以防我有进一步的贡献。

问候。

另一答案

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

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

另一答案

虽然一个老问题所有的贡献帮助了我很多,所以这里是我的解决方案,它在我自己的$stmt = $db->prepare ( "INSERT INTO $table $cols VALUES $rowVals" ); $stmt->execute (); 课程中工作。 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();
}

$ row是一组值数组。在您的情况下,您将调用该函数

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

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

另一答案

这对我有用

insertMultiple("INSERT INTO tbl (`key1`,`key2`)",array(array('r1v1','r1v2'),array('r2v1','r2v2')));
另一答案

这是我

以上是关于PDO Prepared在单个查询中插入多行的主要内容,如果未能解决你的问题,请参考以下文章

使用 PDO Prepared Statement 在 MySQL 中插入 BIT 值

Laravel - PDO Prepared Statement - 在其他无缓冲查询处于活动状态时无法执行查询

MySQL Prepared PDO 语句正在删除反斜杠

在单个事务中插入多行时 Oracle 查询性能下降

MySQL ON DUPLICATE KEY UPDATE 在单个查询中插入多行

使用 PHP PDO 在 SQL 中将多行传递给 UDF