web-dev-qa-db-ja.com

mysqliに多くの値を挿入する最良の方法は?

PHPとMySQLiを使用して、一度に多数の行(2000年頃)を挿入するSQLインジェクションで保護された手法を探しています。
含める必要のあるすべての値を含む配列があります。現在私はそれをやっています:

<?php
$array = array("array", "with", "about", "2000", "values");

foreach ($array as $one) 
{
    $query = "INSERT INTO table (link) VALUES ( ?)";
    $stmt = $mysqli->prepare($query);
    $stmt ->bind_param("s", $one);
    $stmt->execute();
    $stmt->close();
}
?>

call_user_func_array() を試しましたが、スタックオーバーフローが発生しました。

これを行うためのより高速な方法(一度にすべてを挿入するなど)でありながら、SQLインジェクション(プリペアドステートメントなど)やスタックオーバーフローに対して安全な方法は何ですか?
ありがとうございました!

12
Roman Holzner

インサートをトランザクション内に配置することで、速度を大幅に向上させることができるはずです。また、prepareステートメントとbindステートメントをループの外に移動することもできます。

$array = array("array", "with", "about", "2000", "values");
$query = "INSERT INTO table (link) VALUES (?)";
$stmt = $mysqli->prepare($query);
$stmt ->bind_param("s", $one);

$mysqli->query("START TRANSACTION");
foreach ($array as $one) {
    $stmt->execute();
}
$stmt->close();
$mysqli->query("COMMIT");

編集:

このコードをWebサーバーで10,000回繰り返してテストしました。

トランザクションなし:226 seconds.トランザクションあり:2 seconds.またはtwo order of magnitude speed increase、少なくともそのテストでは。

30
Dan Metheus

これをもう一度試してみると、元のコードが小さな変更で機能しない理由がわかりません。

$query = "INSERT INTO table (link) VALUES (?)";
$stmt = $mysqli->prepare($query);
$stmt->bind_param("s", $one);

foreach ($array as $one) {
    $stmt->execute();
}
$stmt->close();
6
Explosion Pills

はい、次のような方法で1つの大きなクエリを手動で作成できます。

$query = "";
foreach ($array as $curvalue) {
  if ($query)
    $query .= ",";
  $query .= "('" . $mysqli->real_escape_string($curvalue) . "')";
}
if ($query) {
  $query = "INSERT INTO table (link) VALUES " . $query;
  $mysqli->query($query);
}
2
Mark Ormston

まず、配列を文字列に変換する必要があります。文字列の配列(2次元配列ではない)である場合、 implode 関数を使用できます。

正しいINSERTステートメントを確保し、SQLインジェクションのリスクを回避するために、各値を括弧で囲み、適切にエスケープする必要があることに注意してください。適切にエスケープするには、PDOを介してMySQLに接続していると仮定して、PDOConnectionの-​​ quote メソッドを使用できます。配列のすべてのエントリでこの操作を実行するには、 array_map を使用できます。

各値をエスケープして単一の文字列に内破した後、それらをINSERTステートメントに入れる必要があります。これは sprintf で実行できます。

例:

<?php
$connection = new PDO(/*...*/);
$connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$dataToBeSaved = [
    'some',
    'data',
    'with "quotes"',
    'and statements\'); DROP DATABASE facebook_main; --'
];


$connection->query(
    sprintf(
        'INSERT INTO table (link) VALUES %s',
        implode(',',
            // for each entry of the array
            array_map(function($entry) use ($connection) { 
                // escape it and wrap it in parenthesis
                return sprintf('(%s)', $connection->quote($entry));
            }, $dataToBeSaved)
        )
    )
);

:データベースに挿入するレコードの量によっては、それらをいくつかのINSERTに分割することをお勧めします。ステートメント。

0
G. Kashtanov