php配列を介してmysqlに複数の行を挿入する


129

挿入コマンドを使用して、PHP経由で大規模なデータセットをMySQLテーブルに渡していますが、1マイルの長い文字列の末尾に各値を追加する以外に、クエリを介して一度に約1000行を挿入できるかどうか疑問に思っています。それを実行します。私はCodeIgniterフレームワークを使用しているので、その機能も利用できます。


Codeigniterの複数行挿入についての質問に応じて、回答を提供しました。
Somnath Muluk

@SomnathMulukありがとうございます。でも、この質問に答える必要があったので、久しぶりです:)...
toofarsideways

CodeIgniterのinsert_batch関数を使用することをお勧めします。ライブラリを使用する場合は、常にその長所とコーディング標準を活用するようにしてください。
Dewald Els 2017

バッチを挿入するのが最善の方法だと思います
Syed Amir Bukhari

回答:


234

INSERTMySQLでは、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句を外部配列に割り当てることもできます。


5
それをありがとう!誰かがそれをコピーすることを計画している場合は、関数の最後に閉じブラケットが欠けていることに注意してください。mysql_real_query( 'INSERT INTO table VALUES(text、category)' .implode( '、'。$ sql));
toofarsideways 2009

3
ありがとう!修繕。(私はよくそうします...)
staticsan

1
クエリは実際に 'INSERT INTO table(text、category)VALUES' .implode( '、'。$ sql) 'sigh 4amコーディングはひどいデバッグにつながる:(
toofarsideways

3
このコードは私の最新プロジェクトのソリューションを作成すると信じています。私の質問は、これはSQLインジェクションから安全ですか?私の計画では、MySQLiを使用していて、これらをPHP5の時点で非推奨にしているのでmysql_real_escape_stringmysqli_real_escape_stringmysql_query一緒に切り替える予定mysqli_queryです。どうもありがとう!
ワードマン2014年

2
mysql_*PHPから削除されたので、mysqli_*インターフェイスを使用してください。
リックジェームズ

60

複数の挿入/バッチ挿入が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')

2
これは、mysql_queryを使用するのではなく、複数行の挿入を行う最も推奨される方法だと思います。フレームワークを使用するときは、常にフレームワークの組み込み機能を使用することをお勧めします。
Praneeth Nidarshan 2018年

22

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)です。


6
私は最近、ここで述べたように、一括挿入と準備済み挿入ステートメントを比較するいくつかのベンチマークを実行しました。約500の挿入の場合、準備された挿入メソッドは2.6〜4.4秒で完了し、一括挿入メソッドは0.12〜0.35秒で完了しました。私はmysqlがこれらの準備されたステートメントをまとめて「バルク」し、バルク挿入と同様に実行するだろうと思ったでしょうが、デフォルトのセットアップでは、明らかにパフォーマンスの違いが非常に大きくなります。(すべてのベンチマーククエリは、自動コミットを防ぐために、各テストの単一トランザクション内で実行されていました)
Motin

16

これは古いクエリであることはわかっていますが、読んでいて、他の場所で見つけたものを追加したいと思っていました。

PHP 5のmysqliは、上記の回答の挿入時間を短縮できるいくつかの優れた機能を備えたオブジェクトです。

$mysqli->autocommit(FALSE);
$mysqli->multi_query($sqlCombined);
$mysqli->autocommit(TRUE);

多くの行を挿入するときに自動コミットをオフにすると、挿入が大幅に高速化されるため、オフにしてから上記のように実行するか、セミコロンで区切られた多くの挿入ステートメントである文字列(sqlCombined)を作成すると、マルチクエリでそれらがうまく処理されます。

これが誰かの時間の節約に役立つことを願っています(検索と挿入!)

R


これは私があなたのアイデアを使用して得たエラーです:「致命的なエラー:30の/homepages/25/d402746174/htdocs/MoneyMachine/saveQuotes.phpのnullでメンバー関数autocommit()を呼び出す」
user3217883

8

あなたは常に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ステートメントを使用するのではなく、一括挿入を実行します。


調べてみましたが、挿入する前にデータを操作する必要があります。これは、1400 x 1400のint値のセットのデカルト積として与えられ、その多くはゼロです。スペースを節約するために中間テーブルを使用して多対多の関係に変換する必要があるため、一括挿入とは対照的に挿入が必要
toosidesideways

ファイルを操作してデータをロードするmysqlステートメントを呼び出した後は、いつでもcsvファイルを生成できます
Alexander Jardim

パスがSQLサーバーではなく、SQLクライアントに対してローカルであることを知っておくと役に立ちます。ファイルはサーバーにアップロードされ、それによって読み取られます。ファイルはすでにサーバー上にある必要があると思いましたが、そうではありません。すでにサーバー上にある場合は、LOCALビットを削除します。
カイル

5

ええと、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()


1
if ifをクエリの上に移動し、if($ k> 0)のように変更すると、よりクリーンになります。
cherouvim

@cherouvim ...まあ、あなたはそれについて正しいです。ご入力いただきありがとうございます。私が提供した例をもう一度読んでいるので、私はあなたの要点を見ることができません。詳しく説明する(ペーストビンなどを使用)。ありがとう
bdl

3
$query= array(); 
foreach( $your_data as $row ) {
    $query[] = '("'.mysql_real_escape_string($row['text']).'", '.$row['category_id'].')';
}
mysql_query('INSERT INTO table (text, category) VALUES '.implode(',', $query));

1

あなたは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));

1

この質問に答えるには遅すぎますが。これが私の答えです。

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番目のパラメーターは値の連想配列です。

query_builderの詳細については、こちらをご覧ください


0

次のように使用される複数行を実行するクラスを作成しました。

$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;
    }
}

0

複数のデータ行を挿入するには、codeigniterでバッチ挿入を使用します。

$this->db->insert_batch('tabname',$data_array); // $data_array holds the value to be inserted

0

私は皆さんが簡単に使用できるこの単純な関数を作成しました。挿入するデータ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));
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.