挿入コマンドを使用して、PHP経由で大規模なデータセットをMySQLテーブルに渡していますが、1マイルの長い文字列の末尾に各値を追加する以外に、クエリを介して一度に約1000行を挿入できるかどうか疑問に思っています。それを実行します。私はCodeIgniterフレームワークを使用しているので、その機能も利用できます。
挿入コマンドを使用して、PHP経由で大規模なデータセットをMySQLテーブルに渡していますが、1マイルの長い文字列の末尾に各値を追加する以外に、クエリを介して一度に約1000行を挿入できるかどうか疑問に思っています。それを実行します。私はCodeIgniterフレームワークを使用しているので、その機能も利用できます。
回答:
INSERT
MySQLでは、1 つのステートメントを複数の行でアセンブルする方が、INSERT
行ごとに1 つのステートメントよりもはるかに高速です。
そうは言っても、PHPで文字列処理の問題が発生しているように思われるかもしれません。これは、実際には言語の問題ではなく、アルゴリズムの問題です。基本的に、大きな文字列を扱う場合、不必要なコピーを最小限に抑える必要があります。これは主に、連結を避けたいことを意味します。一度に数百の行を挿入する場合など、大きな文字列を構築するための最も高速でメモリ効率の良い方法は、implode()
関数と配列の割り当てを利用することです。
$sql = array();
foreach( $data as $row ) {
$sql[] = '("'.mysql_real_escape_string($row['text']).'", '.$row['category_id'].')';
}
mysql_query('INSERT INTO table (text, category) VALUES '.implode(',', $sql));
このアプローチの利点は、各連結でこれまでにアセンブルしたSQLステートメントをコピーして再コピーしないことです。代わりに、PHPはこれをステートメント内で1回実行しimplode()
ます。これは大きな勝利です。
組み合わせる列が多く、1つ以上の列が非常に長い場合は、内部ループを作成して同じことを行いimplode()
、values句を外部配列に割り当てることもできます。
mysql_real_escape_string
、mysqli_real_escape_string
とmysql_query
一緒に切り替える予定mysqli_query
です。どうもありがとう!
mysql_*
PHPから削除されたので、mysqli_*
インターフェイスを使用してください。
複数の挿入/バッチ挿入がcodeigniterでサポートされるようになりました。同じ問題がありました。質問に答えるのはとても遅いですが、誰かを助けるでしょう。それがこの質問に答える理由です。
$data = array(
array(
'title' => 'My title' ,
'name' => 'My Name' ,
'date' => 'My date'
),
array(
'title' => 'Another title' ,
'name' => 'Another Name' ,
'date' => 'Another date'
)
);
$this->db->insert_batch('mytable', $data);
// Produces: INSERT INTO mytable (title, name, date) VALUES ('My title', 'My name', 'My date'), ('Another title', 'Another name', 'Another date')
mysqli_stmtクラスを使用して1つの行を挿入するクエリを準備し、データの配列を反復処理できます。何かのようなもの:
$stmt = $db->stmt_init();
$stmt->prepare("INSERT INTO mytbl (fld1, fld2, fld3, fld4) VALUES(?, ?, ?, ?)");
foreach($myarray as $row)
{
$stmt->bind_param('idsb', $row['fld1'], $row['fld2'], $row['fld3'], $row['fld4']);
$stmt->execute();
}
$stmt->close();
ここで 'idsb'は、バインドするデータのタイプ(int、double、string、blob)です。
これは古いクエリであることはわかっていますが、読んでいて、他の場所で見つけたものを追加したいと思っていました。
PHP 5のmysqliは、上記の回答の挿入時間を短縮できるいくつかの優れた機能を備えたオブジェクトです。
$mysqli->autocommit(FALSE);
$mysqli->multi_query($sqlCombined);
$mysqli->autocommit(TRUE);
多くの行を挿入するときに自動コミットをオフにすると、挿入が大幅に高速化されるため、オフにしてから上記のように実行するか、セミコロンで区切られた多くの挿入ステートメントである文字列(sqlCombined)を作成すると、マルチクエリでそれらがうまく処理されます。
これが誰かの時間の節約に役立つことを願っています(検索と挿入!)
R
あなたは常にmysqlを使うことができますLOAD DATA
:
LOAD DATA LOCAL INFILE '/full/path/to/file/foo.csv' INTO TABLE `footable` FIELDS TERMINATED BY ',' LINES TERMINATED BY '\r\n'
一連のINSERT
ステートメントを使用するのではなく、一括挿入を実行します。
LOCAL
ビットを削除します。
ええと、1000回のクエリ呼び出しを実行したくないのですが、これで問題ありません。
$stmt= array( 'array of statements' );
$query= 'INSERT INTO yourtable (col1,col2,col3) VALUES ';
foreach( $stmt AS $k => $v ) {
$query.= '(' .$v. ')'; // NOTE: you'll have to change to suit
if ( $k !== sizeof($stmt)-1 ) $query.= ', ';
}
$r= mysql_query($query);
データソースによっては、ファイルを開き、を介してコンテンツを配列にダンプするのと同じくらい簡単に配列にデータを入力できますfile()
。
あなたはcodeigniterでいくつかの方法でそれを行うことができます例えば
最初のループ
foreach($myarray as $row)
{
$data = array("first"=>$row->first,"second"=>$row->sec);
$this->db->insert('table_name',$data);
}
2番目 -バッチ挿入
$data = array(
array(
'first' => $myarray[0]['first'] ,
'second' => $myarray[0]['sec'],
),
array(
'first' => $myarray[1]['first'] ,
'second' => $myarray[1]['sec'],
),
);
$this->db->insert_batch('table_name', $data);
3番目の方法-複数の値の受け渡し
$sql = array();
foreach( $myarray as $row ) {
$sql[] = '("'.mysql_real_escape_string($row['first']).'", '.$row['sec'].')';
}
mysql_query('INSERT INTO table (first, second) VALUES '.implode(',', $sql));
この質問に答えるには遅すぎますが。これが私の答えです。
CodeIgniterを使用している場合は、query_builderクラスで定義されている組み込みメソッドを使用できます。
$ this-> db-> insert_batch()
指定したデータに基づいて挿入文字列を生成し、クエリを実行します。配列またはオブジェクトを関数に渡すことができます。以下は、配列を使用した例です。
$data = array(
array(
'title' => 'My title',
'name' => 'My Name',
'date' => 'My date'
),
array(
'title' => 'Another title',
'name' => 'Another Name',
'date' => 'Another date'
)
);
$this->db->insert_batch('mytable', $data);
// Produces: INSERT INTO mytable (title, name, date) VALUES ('My title', 'My name', 'My date'), ('Another title', 'Another name', 'Another date')
最初のパラメーターにはテーブル名が含まれ、2番目のパラメーターは値の連想配列です。
次のように使用される複数行を実行するクラスを作成しました。
$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;
/**
* Create a PDOMultiLine Insert object.
*
* @param PDO $pdo The PDO connection
* @param type $tableName The table name
* @param type $fieldsAsArray An array of the fields being inserted
* @param type $bigInsertCount How many rows to collect before performing an insert.
*/
function __construct(PDO $pdo, $tableName, $fieldsAsArray, $bigInsertCount = 100) {
$this->_numberOfFields = count($fieldsAsArray);
$insertIntoPortion = "REPLACE 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;
}
}
私は皆さんが簡単に使用できるこの単純な関数を作成しました。挿入するデータdata arrayに対してtable-name ($tbl)
、table-field を渡す必要があり($insertFieldsArr)
ます($arr)
。
insert_batch('table',array('field1','field2'),$dataArray);
function insert_batch($tbl,$insertFieldsArr,$arr){ $sql = array();
foreach( $arr as $row ) {
$strVals='';
$cnt=0;
foreach($insertFieldsArr as $key=>$val){
if(is_array($row)){
$strVals.="'".mysql_real_escape_string($row[$cnt]).'\',';
}
else{
$strVals.="'".mysql_real_escape_string($row).'\',';
}
$cnt++;
}
$strVals=rtrim($strVals,',');
$sql[] = '('.$strVals.')';
}
$fields=implode(',',$insertFieldsArr);
mysql_query('INSERT INTO `'.$tbl.'` ('.$fields.') VALUES '.implode(',', $sql));
}