답변:
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 초의 차이 만있었습니다.
placeholders()
계속해서 다시 전화 할 필요가 없습니다 . 루프 전에 한 번 호출하고 루프 내부에 sizeof($datafields)
결과 문자열을 추가하십시오 $question_marks[]
.
발라 타스 씨와 같은 대답입니다.
최신 버전의 MySQL 및 PHP PDO 는 다중 행 INSERT
문을 지원 합니다.
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 코드는 보통 따를 $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();
$rowPlaces
더 이상 필요하지 않습니다.$allPlaces = implode(',', array_fill(0, count($dataVals), '('.str_pad('', (count($colNames)*2)-1, '?,').')'));
votes
ADD UNIQUE unique_index
( user
, email
, address
);
array_push($dataToInsert, ...array_values($dataVals));
속도가 훨씬 빨라질 것입니다foreach ($dataVals as $row => $data) {}
가치있는 것에 대해, 많은 사용자가 선택된 답변이했던 것처럼 단일 문자열 쿼리로 작성하는 대신 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 배 이상 빠릅니다. 이것이 더 많은 열에서 수천 행의 가져 오기를 반영하도록 구축 된 경우 그 차이는 엄청날 수 있습니다.
bind_param
두 번째 가져 오기 루틴에 왜 진술이 없습니까?"입니다.
(?,?)
합니까?
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();
array_push($data, ...array_values($row))
대신에 할 수 있습니다 $data = array_merge($data, array_values($row));
. 훨씬 더 빨리.
array_push()
PHP 4에서도 사용할 수 있습니다.
array_push()
가 인수 압축 풀기를 사용하기 때문입니다. 통지 ...array_values()
가 전화를?
array_values()
는 PHP 4에서도 사용할 수 있습니다 argument unpacking
. 이것이 당신이 무슨 뜻인지 확실하지 않습니다 .
두 가지 가능한 접근 방식 :
$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();
}
모든 행의 데이터가 단일 배열에 있으면 두 번째 솔루션을 사용합니다.
$stmt->execute();
foreach 루프 외부에 있어야한다고 제안 하고 있습니까?
준비된 문장을 사용하는 방식이 아닙니다.
하나의 준비된 명령문을 다른 매개 변수로 여러 번 실행할 수 있으므로 쿼리 당 하나의 행을 삽입하는 것이 좋습니다. 실제로 효율적이고 안전하며 편안한 방식으로 많은 수의 행을 삽입 할 수 있으므로 가장 큰 장점 중 하나입니다.
따라서 적어도 고정 된 수의 행에 대해 제안하는 구성표를 구현하는 것이 가능할 수도 있지만 이것이 실제로 원하는 것이 아니라는 것이 거의 보장됩니다.
더 짧은 대답 : 열 순서로 정렬 된 데이터 배열을 평탄화하십시오.
//$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();
On the readings on PDO, the use prepared statements should give me a better security than static queries.
$workouts_id
못한 $value
데이터 가있을 수 있습니다 . 현재는 아니지만 미래에는 다른 개발자가이 데이터를 안전하지 않다고 보장 할 수 없습니다. 따라서 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;
}
}
이것이 내가 한 방법입니다.
먼저 사용할 열 이름을 정의하거나 비워 두십시오. pdo는 테이블의 모든 열을 사용한다고 가정합니다.이 경우 테이블에 나타나는 순서대로 행 값을 알려야합니다. .
$cols = 'name', 'middleName', 'eMail';
$table = 'people';
이제 2 차원 배열이 이미 준비되었다고 가정합니다. 이를 반복하고 다음과 같이 행 값으로 문자열을 구성하십시오.
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 행으로 테스트되었으며 실행 시간이 늦었습니다. 더 많은 테스트를 수행하고 더 기여할 것이 있으면 여기로 돌아올 것입니다.
문안 인사.
아직 제안되지 않았기 때문에 LOAD DATA INFILE이 여전히 인덱싱을 비활성화하고 모든 데이터를 삽입 한 다음 인덱스를 다시 활성화하여 데이터를로드하는 가장 빠른 방법이라고 확신합니다. 모두 단일 요청으로.
데이터를 csv로 저장하는 것은 fputcsv를 명심해야합니다. MyISAM이 가장 빠르지 만 여전히 InnoDB에서 큰 성능을 얻습니다. 다른 단점도 있지만 100 행 미만으로 많은 데이터를 삽입하고 방해하지 않으면이 경로를 사용합니다.
오래된 질문에 대한 모든 기여는 저에게 큰 도움이되었지만 여기 내 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();
}
$tableName
사용자에게 노출 되었다고 가정 하고 DAL에 있다고 가정 할 수 있습니다 . 당신은 당신의 주장을 확장 할 수 있습니까? 말만하는 것이 도움이되지 않습니다.
$tableName
?
이 문제에 대한 또 다른 (슬림) 해결책이 있습니다.
먼저 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();
이 함수를 사용하여 단일 쿼리에 여러 행을 삽입 할 수 있습니다.
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')));
이는 단일 쿼리로 여러 행을 삽입하는 동안 준비된 명령문 을 사용하는 이점이 있습니다 . 보안!
여기 내 해결책이 있습니다 : auraphp / Aura.Sql 라이브러리를 기반으로 한 https://github.com/sasha-ch/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보다 훨씬 낫습니다).
test.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));
?>
Database.php
<?php
class Database
{
/* Initializing Database Information */
var $host = 'localhost';
var $user = 'root';
var $pass = '';
var $database = "database";
var $dbh;
/* Connecting Datbase */
public function __construct(){
try {
$this->dbh = new PDO('mysql:host='.$this->host.';dbname='.$this->database.'', $this->user, $this->pass);
//print "Connected Successfully";
}
catch (PDOException $e) {
print "Error!: " . $e->getMessage() . "<br/>";
die();
}
}
/* Insert Multiple Rows in a table */
public function insertMultiple($table,$rows){
$this->dbh->beginTransaction(); // also helps speed up your inserts.
$insert_values = array();
foreach($rows as $d){
$question_marks[] = '(' . $this->placeholders('?', sizeof($d)) . ')';
$insert_values = array_merge($insert_values, array_values($d));
$datafields = array_keys($d);
}
$sql = "INSERT INTO $table (" . implode(",", $datafields ) . ") VALUES " . implode(',', $question_marks);
$stmt = $this->dbh->prepare ($sql);
try {
$stmt->execute($insert_values);
} catch (PDOException $e){
echo $e->getMessage();
}
return $this->dbh->commit();
}
/* placeholders for prepared statements like (?,?,?) */
function placeholders($text, $count=0, $separator=","){
$result = array();
if($count > 0){
for($x=0; $x<$count; $x++){
$result[] = $text;
}
}
return implode($separator, $result);
}
}
?>
나는 똑같은 문제를 겪었고 이것이 내가 스스로 성취하는 방법이며, 그것을 위해 스스로 기능을 만들었습니다.
예:
INSERT INTO 국가 (국가, 도시) 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;
}
경우 insertMultipleData ($ 테이블 $ multi_params)가 TRUE를 반환 , 데이터가 데이터베이스에 삽입되었습니다.
내 실험에 따르면 단일 트랜잭션에서 여러 값 행을 가진 mysql insert 문이 가장 빠릅니다.
그러나 데이터가 너무 많은 경우 mysql의 max_allowed_packet
설정은 여러 값 행으로 단일 트랜잭션 삽입을 제한 할 수 있습니다. 따라서 mysql의 max_allowed_packet
크기 보다 큰 데이터가 있으면 다음 함수가 실패 합니다.
singleTransactionInsertWithRollback
singleTransactionInsertWithPlaceholders
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
이것은 나를 위해 일했다
$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();
이 같은 것은 어떻습니까 :
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 number"를 추가하는 동시에 바인딩 매개 변수의 값을 배열에 추가하는 것입니다. 배열에서 "키"인덱스를 사용하지 않으려면 루프 내에 $ i = 0 및 $ i ++를 추가 할 수 있습니다. 이 예제에서는 이름이 지정된 키가있는 연관 배열이 있어도 키가 고유 한 경우에도 작동합니다. 약간의 작업으로 중첩 배열에도 좋습니다.
** substr은 $ sql 변수의 마지막 공백과 쉼표를 제거합니다. 공백이 없으면 -2 대신 -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);
}
$stmt->execute($data);
php.net/manual/en/에 대한 많은 답변에 주의하십시오. 기본적으로 모든 매개 변수는 문자열로 유효성 검사를 통과합니다. 쿼리를 작성하고 수동으로bindValue
또는bindParam
유형을 세 번째 인수로 전달한 후 데이터를 반복하십시오 .