PDO 준비됨 단일 쿼리에 여러 행을 삽입합니다.
현재 MySQL에서 다음과 같은 유형의 SQL을 사용하여 단일 쿼리에 여러 행의 값을 삽입하고 있습니다.
INSERT INTO `tbl` (`key1`,`key2`) VALUES ('r1v1','r1v2'),('r2v1','r2v2'),...
PDO에 대한 판독치에 대해서는 use prepared 스테이트먼트를 사용하면 정적 쿼리보다 보안이 강화됩니다.
따라서 준비된 문장을 사용하여 "1개의 쿼리를 사용하여 여러 행의 값을 삽입"할 수 있는지 알고 싶습니다.
그렇다면 어떻게 구현해야 하는지 알 수 있을까요?
PDO 준비 스테이트먼트를 사용한 여러 값 삽입
하나의 실행 문에 여러 값을 삽입합니다.이유는 이 페이지에 따르면 일반 삽입보다 빠르기 때문입니다.
$datafields = array('fielda', 'fieldb', ... );
$data[] = array('fielda' => 'value', 'fieldb' => 'value' ....);
$data[] = array('fielda' => 'value', 'fieldb' => 'value' ....);
데이터 값이 증가하거나 데이터를 채우는 루프가 있을 수 있습니다.
미리 준비한 삽입을 사용할 경우 삽입할 필드 및 매개 변수를 바인딩할 ? 자리 표시자를 만들 필드 수를 알아야 합니다.
insert into table (fielda, fieldb, ... ) values (?,?...), (?,?...)....
기본적으로는 이렇게 삽입문이 나타나기를 바랍니다.
이제, 코드는 다음과 같습니다.
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);
$stmt->execute($insert_values);
$pdo->commit();
제 테스트에서는 여러 개의 인서트와 하나의 값을 가진 일반적인 준비된 인서트를 사용할 때 1초밖에 차이가 나지 않았습니다.
발라그타스 씨와 같은 대답입니다. 조금 더 명확하고...
최신 버전의 MySQL 및 PHP PDO는 다중 행을 지원합니다.INSERT
★★★★★★★★★★★★★★★★★★.
SQL의 개요
. 3열 표를 SQL로 만들고 3열 표라고 요.INSERT
discloss.to.discloss.
INSERT INTO tbl_name
(colA, colB, colC)
VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?) [,...]
ON DUPLICATE KEY UPDATE
인서트하다음음 츠키
ON DUPLICATE KEY UPDATE colA = VALUES(colA), colB = VALUES(colB), colC = VALUES(colC)
PHP의 개요
코드는 입니다.$pdo->prepare($qry)
★★★★★★★★★★★★★★★★★」$stmt->execute($params)
PDO »
$params
모든 값의 1차원 배열이 됩니다.INSERT
.
위의 예에서는 9개의 요소를 포함해야 합니다.PDO는 3개의 모든 세트를 값의 단일 행으로 사용합니다.(3개 열씩 3개 행 삽입 = 9개 요소 배열).
실행
아래 코드는 효율이 아닌 명확성을 위해 작성되었습니다. PHP array_*()
필요에 따라 데이터를 매핑하거나 탐색할 수 있는 기능을 제공합니다.트랜잭션을 사용할 수 있는지 여부는 MySQL 테이블 유형에 따라 달라집니다.
전제 조건:
$tblName
-$colNames
- 열이 열 열. - 이 이 아닌 경우 - 는 MySQL 열 식별자입니다. 그렇지 않으면 백틱(')으로 이스케이프하십시오.$dataVals
- array. 서 각 요소는 INSERT-mutli-dimension 배열에 값
샘플 코드
// 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);
$stmt->execute($dataToInsert);
$pdo->commit();
즉, 많은 사용자가 선택한 답변처럼 단일 문자열 쿼리로 구축하지 않고 INSERT 문을 통해 반복할 것을 권장합니다.2개의 필드와 매우 기본적인 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배 이상 빨랐습니다.이것이 수천 행의 Import를 더 많은 열에 반영하도록 설계되었다면 그 차이는 엄청날 수 있습니다.
Herbert Balagtas의 Accepted Answer는 $data 배열이 작을 때 제대로 작동합니다.더 큰 $data 배열에서는 array_merge 함수가 엄청나게 느려집니다.$data 어레이를 만들기 위한 테스트 파일에는 28개의 col이 있으며 약 80,000줄입니다.마지막 대본을 완성하는 데 41초가 걸렸다.
array_merge() 대신 array_push()를 사용하여 $insert_values를 작성하면 실행 시간이 0.41초로 100배 빨라집니다.
문제가 있는 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()의 필요성을 없애기 위해 대신 다음 2개의 어레이를 구축할 수 있습니다.
//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);
$stmt->execute($insert_values);
$pdo->commit();
다음 두 가지 방법이 있습니다.
$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();
}
모든 행의 데이터가 단일 배열에 있는 경우 두 번째 솔루션을 사용합니다.
그것은 단순히 준비된 문장을 사용하는 방식이 아닙니다.
다른 파라미터로1개의 준비된 스테이트먼트를 여러 번 실행할 수 있기 때문에 쿼리당1개의 행을 삽입해도 문제 없습니다.사실 이는 효율적이고 안전하며 편안한 방법으로 많은 열을 삽입할 수 있는 가장 큰 장점 중 하나입니다.
따라서 적어도 일정 수의 행에 대해 제안 스킴을 구현할 수 있지만, 이것이 실제로 원하는 것이 아니라는 것은 거의 확실합니다.
더 짧은 답변: 열별로 정렬된 데이터 배열을 평평하게 합니다.
//$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";
1,000개 정도의 레코드를 삽입할 때는 모든 레코드를 루프하여 삽입할 필요가 없습니다.필요한 것은 값의 카운트뿐입니다.
여기 저의 간단한 방법이 있습니다.
$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();
다음은 퍼지 옵션을 사용하여 여러 삽입을 수행하는 강의입니다.
<?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;
}
}
실험 결과 단일 트랜잭션에 여러 개의 값 행이 있는 mysql insert 문이 가장 빠르다는 것을 알게 되었습니다.
의 mysql, mysql이 .max_allowed_packet
설정 시 여러 개의 값 행이 있는 단일 트랜잭션 삽입이 제한될 수 있습니다.의 mysql보다 큰는 실패합니다.max_allowed_packet
표시:
singleTransactionInsertWithRollback
singleTransactionInsertWithPlaceholders
singleTransactionInsert
대용량 데이터 삽입 시나리오에서 가장 성공적인 시나리오는 다음과 같습니다.transactionSpeed
단, 의 methods는 시간이 더 소요됩니다.따라서 이 를 더 하여 단일 번하거나 를 사용하여 할 수 .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
이 문제에 대한 다른 (슬림)솔루션을 다음에 나타냅니다.
처음에 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();
저는 이렇게 했습니다.
먼저 사용할 열 이름을 정의하거나 공백으로 두면 pdo는 테이블의 모든 열을 사용할 것으로 가정합니다.이 경우 표에 표시되는 행 값을 정확한 순서로 알려야 합니다.
$cols = 'name', 'middleName', 'eMail';
$table = 'people';
이제 2차원 배열이 이미 준비되었다고 가정해 보겠습니다.반복하여 다음과 같이 행 값을 사용하여 문자열을 작성합니다.
foreach ( $people as $person ) {
if(! $rowVals ) {
$rows = '(' . "'$name'" . ',' . "'$middleName'" . ',' . "'$eMail'" . ')';
} else { $rowVals = '(' . "'$name'" . ',' . "'$middleName'" . ',' . "'$eMail'" . ')';
}
방금 전에 $rows가 이미 정의되어 있는지 확인하고, 정의되어 있지 않은 경우 $rows를 생성하여 행 값과 필요한 SQL 구문을 저장하여 주십시오.문자열은 큰따옴표와 작은따옴표 안에 들어가야 하므로 즉시 인식됩니다.
이제 스테이트먼트를 준비하고 실행하기만 하면 됩니다.
$stmt = $db->prepare ( "INSERT INTO $table $cols VALUES $rowVals" );
$stmt->execute ();
지금까지 최대 2000행의 행으로 테스트했는데 실행 시간이 너무 짧습니다.몇 가지 테스트를 더 해보고 내가 더 도움이 필요할 때를 대비해서 여기로 돌아올게.
안부 전해요.
아직 제안되지 않았기 때문에 LOAD DATA INFILE은 인덱스를 비활성화하고 모든 데이터를 삽입한 다음 인덱스를 다시 활성화하기 때문에 여전히 데이터를 로드하는 가장 빠른 방법이라고 확신합니다.
데이터를 csv로 저장하는 것은 fputcsv에 유의해야 합니다.MyISAM이 가장 빠르지만 InnoDB에서는 여전히 큰 성능을 얻을 수 있습니다.단, 다른 단점도 있기 때문에 데이터를 많이 삽입할 경우에는 100줄 미만으로 번거롭게 할 필요가 없습니다.
도움이 , 기고문, 기고문, 기고문, 기고문, 기고문, 기고문, 기고문, 기고문, 기고문, 기고문, 기고문, 기고문, 기고문, 기고문, , 기고문, 기고문, 기고문, 기고문, 기고문, 기고문, 기고문, 기고문, 기고문, 기고문, 기고문, 기고문, 기고문, 기고문, , 기고문, 기고DbContext
ㅇㅇㅇㅇㅇ.$rows
는 행 모델을 입니다.예를 들어, 행 또는 모델을 나타냅니다.field name => insert value
.
때 잘 , 모델 데이터를 전달받았을 때 해당 패턴을 사용합니다.ToRowArray
모델 클래스 내의 메서드.
주의: 이 메서드에 전달된 인수가 사용자에게 공개되거나 사용자 입력(인서트 값 이외)에 의존하여 검증 및 삭제되는 것은 말할 필요도 없습니다.그
$tableName
를 들어, 「Calling Logic」은 「Calling Logic」입니다.★★★★★★★★★★★★★★★★★,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();
}
다음 함수를 사용하여 단일 쿼리에 여러 행을 삽입할 수 있습니다.
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')));
이렇게 하면 단일 쿼리로 여러 행을 삽입하면서 준비된 문을 사용할 수 있다는 이점이 있습니다.보안!
다음은 저의 해결책입니다. 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);
버그 리포트는 환영입니다.
모든 독일어 포스트코드를 빈 테이블에 삽입하는 실제 예(나중에 도시 이름을 추가하는 경우):
// 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개보다 훨씬 좋습니다).
이건 내게 효과가 있었다.
$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();
준비된 쿼리를 작성하기 위해 여기에 제시된 솔루션의 대부분은 필요한 것보다 복잡합니다.PHP의 내장 함수를 사용하면 큰 오버헤드 없이 SQL 문을 쉽게 만들 수 있습니다.
의 「」$records
배열인 「」의)field => value
는 지정된 $table
PDO가$connection
하나의 준비된 스테이트먼트만 사용합니다.는 PHP 5.6+에 대한 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);
}
저도 같은 문제가 있어서 이렇게 해서 제 자신을 위한 기능을 만들었습니다(그것이 도움이 된다면 사용해도 좋습니다).
예:
국가(국가, 도시)의 가치(독일, 베를린), (프랑스, 파리)에 삽입;
$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;
}
insert Multiple Data($table, $multi_params)가 TRUE를 반환하면 데이터가 데이터베이스에 삽입된 것입니다.
이런 건 어때?
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 번호"를 추가하는 동시에 바인딩 파라미터의 값을 배열에 추가하는 것입니다.배열에서 "key" 인덱스를 사용하지 않으려면 루프 내부에 $i=0 및 $i++를 추가할 수 있습니다.이 예에서는, 어느쪽이든, 이름이 붙은 키를 가지는 관련 배열이 있는 경우에서도, 키가 일의인 경우는 동작합니다.조금만 작업하면 네스트된 어레이도 문제 없습니다.
** $sql 변수 마지막 공백과 쉼표는 삭제되므로 공백이 없는 경우 -2가 아닌 -1로 변경해야 합니다.
언급URL : https://stackoverflow.com/questions/1176352/pdo-prepared-inserts-multiple-rows-in-single-query
'itsource' 카테고리의 다른 글
mysql 데이터베이스에 슈퍼 권한을 추가하는 방법 (0) | 2023.01.19 |
---|---|
하위 행을 추가하거나 업데이트할 수 없음: 외부 키 제약 조건이 실패합니다. (0) | 2023.01.19 |
MySQL이 "데이터 전송 중" 상태이면 어떤 의미입니까? (0) | 2023.01.19 |
열이 다른 열에서 계산되었습니까? (0) | 2023.01.19 |
mysql LIKE는 어떻게 대소문자를 구분합니까? (0) | 2023.01.15 |