web-dev-qa-db-ja.com

PDOプリペアドステートメントで複数の行を挿入します

1つのプリペアドステートメントを使用して複数の行を挿入できるかどうかを知りたいのですが。以下は、通常1行をデータベースに挿入する方法の例です。

$params=array();
$params[':val1']="val1";
$params[':val2']="val2";
$params[':val3']="val3";
$sql="INSERT INTO table VALUES (col1,col2,col3) VALUES (:val1,:val2,:val3)";
$stmt=DB::getInstance()->prepare($sql);
$stmt->execute($params);

挿入したい値は、配列から取得されます。例:$ values [0] ['val1']; $ values [0] ['val2']; $ values [0] ['val3']; $ values [1] ['val1']; $ values [2] ['val2'];

等.

このコードは一度に数百行を挿入する必要があるかもしれません。数百のパラメーターを作成するループを作成してから、各行に追加の挿入をSQLステートメントに追加することを考えましたが、もっと良い方法があるはずだと思いました。これを行うための最良の方法は何でしょうか?

13
ezero

最初に重要なことは、oneINSERTクエリ

INSERT INTO Table (col1, col2, col3) 
VALUES ('abc', 'def', 'ghi'),
       ('abc', 'def', 'ghi'),
       ('abc', 'def', 'ghi'),
       ('abc', 'def', 'ghi'),
       ('abc', 'def', 'ghi')
       -- and so on...

それがわかれば、(たとえば)PDOで優れたソリューションを得ることができます。
完全なprepareおよびexecuteプロセスが必要であることを覚えておく必要があります(セキュリティの観点から、各パラメーターを個別に渡す必要があります)。

次のように構造化された挿入する行があるとします。

$rows = array(
              array('abc', 'def', 'ghi'), // row 1 to insert
              array('abc', 'def', 'ghi'), // row 2 to insert
              array('abc', 'def', 'ghi')  // row 3 to insert
              // and so on ...
);

あなたの目標は、この結果を準備されたクエリとして取得することです。

INSERT INTO Table (col1, col2, col3) 
VALUES (?, ?, ?),
       (?, ?, ?),
       (?, ?, ?)

対応するexecute

PDOStatement::execute(array('abc', 'def', 'ghi', 'abc', 'def', 'ghi', 'abc', 'def', 'ghi'));


まあ、あなただけは今それをしなければなりません:

$rows = array(
              array('abc', 'def', 'ghi'),
              array('abc', 'def', 'ghi'),
              array('abc', 'def', 'ghi')
);

$row_length = count($rows[0]);
$nb_rows = count($rows);
$length = $nb_rows * $row_length;

/* Fill in chunks with '?' and separate them by group of $row_length */
$args = implode(',', array_map(
                                function($el) { return '('.implode(',', $el).')'; },
                                array_chunk(array_fill(0, $length, '?'), $row_length)
                            ));

$params = array();
foreach($rows as $row)
{
   foreach($row as $value)
   {
      $params[] = $value;
   }
}

$query = "INSERT INTO Table (col1, col2, col3) VALUES ".$args;
$stmt = DB::getInstance()->prepare($query);
$stmt->execute($params);

以上です!

このように、各パラメータは個別に処理されます。これは、必要なもの(セキュリティ、セキュリティ、セキュリティ!)であり、すべてを動的に1つのINSERTクエリで処理します。


挿入する行が多すぎる場合( this を参照)、1つずつexecuteする必要があります

$rows = array(
              array('abc', 'def', 'ghi'), // row 1 to insert
              array('abc', 'def', 'ghi'), // row 2 to insert
              array('abc', 'def', 'ghi')  // row 3 to insert
              // and so on ...
);

$args = array_fill(0, count($rows[0]), '?');

$query = "INSERT INTO Table (col1, col2, col3) VALUES (".implode(',', $args).")";
$stmt = $pdo->prepare($query);

foreach ($rows as $row) 
{
   $stmt->execute($row);
}
27
Justin Iurman

数百行しか挿入しない場合は、次のような単純なコードをお勧めします。単一行のINSERTステートメントを準備してから、データ配列をループし、準備されたクエリを各行に対して1回実行します。

$rows = array(
              array('abc', 'def', 'ghi'), // row 1 to insert
              array('abc', 'def', 'ghi'), // row 2 to insert
              array('abc', 'def', 'ghi')  // row 3 to insert
              // and so on ...
);

$params = implode(",", array_fill(0, count($rows[0]), "?"));

$sql = "INSERT INTO mytable VALUES ($params)";

$stmt = $pdo->prepare($sql); // rely on exceptions for error detection

foreach ($rows as $row) {
    $stmt->execute($row);
}

もちろん、MySQLは複数行のINSERT構文をサポートしているので、それをまとめてみることができます。

$params = implode(",", array_fill(0, count($rows[0]), "?"));

$tuples = "(" . implode("),(", array_fill(0, count($rows), $params)) . ")";

$sql = "INSERT INTO mytable VALUES $tuples";

$values = call_user_func_array("array_merge", $rows);

$stmt = $pdo->prepare($sql);

$stmt->execute($values);

ただし、データ配列内の項目と同じ数のタプルを使用して単一のINSERTステートメントを作成しようとすると、 最大パケット長 よりも長いSQLステートメントが誤って生成される可能性があります。

数千の行があり、準備されたステートメントを一度に1行実行するのに十分なオーバーヘッドがある場合は、 LOAD DATA INFILE を使用する必要があります。

6
Bill Karwin

テーブルがトランザクション型(たとえば、InnoDB)の場合は、 Transaction を使用して挿入を高速化できます。トランザクションには、 ロールバック という利点もあります。

$pdo = DB::getInstance();
$stmt = $pdo->prepare('INSERT INTO table VALUES (col1, col2, col3) VALUES (:val1, :val2, :val3)');

$pdo->beginTransaction();

// The queries are not executed yet, but pushed to a transaction "stack"
foreach ($values as $value) {
    $stmt->execute([
        ':val1' => $value['val1'],
        ':val2' => $value['val2'],
        ':val3' => $value['val3'],
    ]);
}

// Executes all the queries "at once"
$pdo->commit();
5
Lucas Kahlert

コードを保持するには、必要なすべての挿入を実行するためのループを作成する必要があります。

_$array_params = array();
$params[':val1']="val1 1";
$params[':val2']="val1 2";
$params[':val3']="val1 3";
$array_params[] = $params;

$params[':val1']="val2 1";
$params[':val2']="val2 2";
$params[':val3']="val2 3";
$array_params[] = $params;

$sql="INSERT INTO table (col1,col2,col3) VALUES (:val1,:val2,:val3)";
$stmt=DB::getInstance()->prepare($sql);
foreach($array_params as $params)
{
  $stmt->execute($params);
}
_

ただし、次のようなものを使用してクエリを作成することにより、INSERT INTO table (col1,col2,col3) VALUES ("val1","val2","val3"),("val4","val5","val6"),("val7","val8,"val9")のような1つのクエリで複数の挿入を実行することができます。

_$all_inserts = array( array('val1', 'val2', 'val3'),array('val4', 'val5', 'val6'));
$sql = 'INSERT INTO table (col1,col2,col3) VALUES ';
$rows = array();
foreach ($all_inserts as $one_insert)
{
   $rows[] = '('.implode(',', $pdo->quote($one_insert).')';
}
$sql .= ' '.implode(',', $rows);
$pdo->query($sql); 
_
3
Asenar