현재 MySQL 에서이 유형의 SQL을 사용하여 단일 쿼리에 여러 행의 값을 삽입하고 있습니다.
INSERT INTO `tbl` (`key1`,`key2`) VALUES ('r1v1','r1v2'),('r2v1','r2v2'),...
PDO에 대한 독서에서 사용 준비된 문은 정적 쿼리보다 더 나은 보안을 제공해야합니다.
따라서 준비된 문을 사용하여 “하나의 쿼리를 사용하여 여러 행의 값 삽입”을 생성 할 수 있는지 알고 싶습니다.
그렇다면 어떻게 구현할 수 있습니까?
답변
PDO 준비된 명령문으로 다중 값 삽입
하나의 execute 문에 여러 값을 삽입합니다. 이 페이지 에 따르면 일반 인서트보다 빠르기 때문입니다.
$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 초의 차이 만있었습니다.
답변
발라 타스 씨와 같은 대답입니다.
최신 버전의 MySQL 및 PHP PDO 는 다중 행 INSERT
문을 지원 합니다.
SQL 개요
3 열 테이블을 가정하면 SQL은 다음과 같이 보입니다 INSERT
.
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 개요
귀하의 PHP 코드는 보통 따를 $pdo->prepare($qry)
와 $stmt->execute($params)
PDO 전화를.
$params
에 전달할 모든 값 의 1 차원 배열입니다 INSERT
.
위의 예에서는 9 개의 요소를 포함해야합니다. PDO는 3의 모든 세트를 단일 값 행으로 사용합니다. (각 3 열의 3 행 삽입 = 9 요소 배열)
이행
아래 코드는 효율성이 아니라 명확성을 위해 작성되었습니다. array_*()
원하는 경우 데이터를 더 잘 매핑하거나 살펴 보는 더 나은 방법을 위해 PHP 함수를 사용하십시오. 트랜잭션을 사용할 수 있는지 여부는 MySQL 테이블 유형에 따라 다릅니다.
가정 :
$tblName
-INSERT 할 테이블의 문자열 이름$colNames
-테이블의 열 이름의 1 차원 배열이 열 이름은 유효한 MySQL 열 식별자 여야합니다. 그렇지 않으면 백틱 (“)으로 이스케이프 처리$dataVals
-각 요소가 INSERT 할 값 행의 1 차원 배열 인 다중 차원 배열
샘플 코드
// 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();
답변
가치있는 것에 대해, 많은 사용자가 선택된 답변이했던 것처럼 단일 문자열 쿼리로 작성하는 대신 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>";
?>
전체 쿼리 자체가 밀리 초 이하인 반면 후자 (단일 문자열) 쿼리는 일관되게 8 배 이상 빠릅니다. 이것이 더 많은 열에서 수천 행의 가져 오기를 반영하도록 구축 된 경우 그 차이는 엄청날 수 있습니다.
답변
Herbert Balagtas의 대답은 $ data 배열이 작을 때 잘 작동합니다. 더 큰 $ data 배열에서는 array_merge 함수가 엄청나게 느려집니다. $ data 배열을 만드는 테스트 파일에는 28 열이 있으며 약 80,000 줄입니다. 마지막 스크립트는 41 초가 걸렸 습니다.
사용 array_push 것은 () 대신 array_merge의 $ INSERT_VALUES를 만드는 ()는 결과 100 배 속도까지 의 실행 시간 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();
답변
두 가지 가능한 접근 방식 :
$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();
}
모든 행의 데이터가 단일 배열에 있으면 두 번째 솔루션을 사용합니다.
답변
준비된 문장을 사용하는 방식이 아닙니다.
하나의 준비된 명령문을 다른 매개 변수로 여러 번 실행할 수 있으므로 쿼리 당 하나의 행을 삽입하는 것이 좋습니다. 실제로 효율적이고 안전하며 편안한 방식으로 많은 수의 행을 삽입 할 수 있으므로 가장 큰 장점 중 하나입니다.
따라서 적어도 고정 된 수의 행에 대해 제안하는 구성표를 구현하는 것이 가능할 수도 있지만 이것이 실제로 원하는 것이 아니라는 것이 거의 보장됩니다.
답변
더 짧은 대답 : 열 순서로 정렬 된 데이터 배열을 평탄화하십시오.
//$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 개 정도의 레코드를 삽입 할 때 필요한 모든 값이 필요할 때 레코드를 삽입하기 위해 모든 레코드를 반복 할 필요는 없습니다.