hob*_*all 136 php pdo insert prepared-statement
我目前在MySQL上使用这种类型的SQL在一个查询中插入多行值:
INSERT INTO `tbl` (`key1`,`key2`) VALUES ('r1v1','r1v2'),('r2v1','r2v2'),...
Run Code Online (Sandbox Code Playgroud)
在PDO上的读数中,使用预处理语句应该比静态查询具有更好的安全性.
因此,我想知道是否可以使用预准备语句生成"通过使用一个查询插入多行值".
如果是,我可以知道如何实施它?
小智 142
使用PDO准备语句插入多个值
在一个execute语句中插入多个值.为什么,因为根据此页面,它比常规插入更快.
$datafields = array('fielda', 'fieldb', ... );
$data[] = array('fielda' => 'value', 'fieldb' => 'value' ....);
$data[] = array('fielda' => 'value', 'fieldb' => 'value' ....);
Run Code Online (Sandbox Code Playgroud)
更多数据值,或者您可能有一个填充数据的循环.
使用准备好的插入,您需要知道要插入的字段以及要创建的字段数?占位符绑定您的参数.
insert into table (fielda, fieldb, ... ) values (?,?...), (?,?...)....
Run Code Online (Sandbox Code Playgroud)
这基本上就是我们想要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();
Run Code Online (Sandbox Code Playgroud)
虽然在我的测试中,当使用多个插入物和常规准备的单值插入物时,只有1秒的差异.
jam*_*svl 70
和Balagtas先生一样的答案,更清楚......
最新版本的MySQL和PHP PDO 也支持多行INSERT语句.
SQL将看起来像这样,假设你想要一个3列表INSERT.
INSERT INTO tbl_name
(colA, colB, colC)
VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?) [,...]
Run Code Online (Sandbox Code Playgroud)
ON DUPLICATE KEY UPDATE即使使用多行INSERT,也能正常工作; 追加这个:
ON DUPLICATE KEY UPDATE colA = VALUES(colA), colB = VALUES(colB), colC = VALUES(colC)
Run Code Online (Sandbox Code Playgroud)
您的PHP代码将遵循常规$pdo->prepare($qry)和$stmt->execute($params)PDO调用.
$params将是传递给的所有值的一维数组INSERT.
在上面的例子中,它应该包含9个元素; PDO将使用每组3作为单行值.(插入3行,每列3列= 9个元素数组.)
下面的代码是为了清晰起见,而不是效率.array_*()如果您愿意,可以使用PHP 函数更好地映射或遍历数据.您是否可以使用事务显然取决于您的MySQL表类型.
假设:
$tblName - 要INSERT到的表的字符串名称$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();
Run Code Online (Sandbox Code Playgroud)
JM4*_*JM4 39
对于它的价值,我看到很多用户建议迭代INSERT语句,而不是像所选答案那样构建为单个字符串查询.我决定只用两个字段和一个非常基本的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>";
?>
Run Code Online (Sandbox Code Playgroud)
虽然整个查询本身花费了毫秒或更短的时间,但后者(单个字符串)查询的速度始终快8倍或更多.如果这是为了反映在更多列上导入数千行,那么差异可能是巨大的.
小智 33
当$ data数组较小时,Herbert Balagtas的接受答案很有效.对于较大的$ data数组,array_merge函数变得非常慢.我创建$ data数组的测试文件有28个cols,大约是80,000行.最后的剧本需要41秒才能完成.
使用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));
}
Run Code Online (Sandbox Code Playgroud)
为了消除对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 );
Run Code Online (Sandbox Code Playgroud)
然后可以按如下方式使用这些数组:
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();
Run Code Online (Sandbox Code Playgroud)
Zyx*_*Zyx 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();
Run Code Online (Sandbox Code Playgroud)
要么:
$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();
}
Run Code Online (Sandbox Code Playgroud)
如果所有行的数据都在一个数组中,我将使用第二个解决方案.
seb*_*sgo 12
这根本不是你使用预准备语句的方式.
每个查询插入一行是完全可以的,因为您可以使用不同的参数多次执行一个预准备语句.事实上,这是最大的优势之一,因为它允许您以高效,安全和舒适的方式插入大量行.
因此,可能可以实现您提议的方案,至少对于固定数量的行,但几乎可以保证这不是您想要的.
一个较短的答案:然后平整按列排序的数据数组
//$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";
Run Code Online (Sandbox Code Playgroud)
当插入1000个左右的记录时,您不希望必须循环遍历每个记录以在需要的时候插入它们是值的计数.
小智 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();
Run Code Online (Sandbox Code Playgroud)