一括挿入ステートメントクエリをその場で作成することで準備することは可能ですが、いくつかのトリックが必要です。最も重要なビットはstr_pad()
を使用することです 可変長のクエリ文字列を作成し、call_user_func_array()
を使用します bind_param()
を呼び出す 可変数のパラメーターを使用します。
function insertBulkPrepared($db, $table, $fields, $types, $values) {
$chunklength = 500;
$fieldcount = count($fields);
$fieldnames = '`'.join('`, `', $fields).'`';
$prefix = "INSERT INTO `$table` ($fieldnames) VALUES ";
$params = '(' . str_pad('', 3*$fieldcount - 2, '?, ') . '), ';
$inserted = 0;
foreach (array_chunk($values, $fieldcount*$chunklength) as $group) {
$length = count($group);
if ($inserted != $length) {
if ($inserted) $stmt->close();
$records = $length / $fieldcount;
$query = $prefix . str_pad('', 3*$length + 2*($records - 1), $params);
#echo "\n<br>Preparing '" . $query . "'";
$stmt = $db->prepare($query);
if (!$stmt) return false;
$binding = str_pad('', $length, $types);
$inserted = $length;
}
array_unshift($group, $binding);
#echo "\n<br>Binding " . var_export($group, true);
$bound = call_user_func_array(array($stmt, 'bind_param'), $group);
if (!$bound) return false;
if (!$stmt->execute()) return false;
}
if ($inserted) $stmt->close();
return true;
}
この関数は$db
を受け取ります mysqli
として インスタンス、テーブル名、フィールド名の配列、および値への参照のフラット配列。クエリごとに最大500レコードを挿入し、可能な場合はプリペアドステートメントを再利用します。 true
を返します すべての挿入が成功した場合、またはfalse
それらのいずれかが失敗した場合。警告:
- テーブル名とフィールド名はエスケープされません。バックティックが含まれていないことを確認するのはあなたに任せます。幸いなことに、ユーザー入力から取得することはできません。
-
$values
の長さの場合$fields
の長さの倍数ではありません 、最終チャンクはおそらく準備段階で失敗します。 - 同様に、
$types
の長さ パラメータは$fields
の長さと一致する必要があります ほとんどの場合、特にそれらのいくつかが異なる場合。 - 失敗する3つの方法を区別しません。また、成功した挿入の数を追跡したり、エラー後に続行しようとしたりすることもありません。
この関数を定義すると、サンプルコードを次のように置き換えることができます。
$inserts = array();
for ($j = 0; $j < $abilitiesMax - 2; $j++) {
$inserts[] = &$abilityArray[$i]['match_id'];
$inserts[] = &$abilityArray[$i]['player_slot'];
$inserts[] = &$abilityArray[$i][$j]['ability'];
$inserts[] = &$abilityArray[$i][$j]['time'];
$inserts[] = &$abilityArray[$i][$j]['level'];
}
$fields = array('match_id', 'player_slot', 'ability', 'time', 'level');
$result = insertBulkPrepared($db, 'abilities', $fields, 'iiiii', $inserts);
if (!$result) {
echo "<p>$db->error</p>";
echo "<p>ERROR: when trying to insert abilities query</p>";
}
mysqli_stmt::bind_param
であるため、これらのアンパサンドは重要です。 call_user_func_array
によって提供されない参照を想定しています PHPの最近のバージョンでは。
元のプリペアドステートメントを提供しなかったため、おそらくテーブル名とフィールド名を調整する必要があります。また、コードが$i
のループ内にあるように見えます;その場合、for
のみ ループは外側のループの内側にある必要があります。ループの外側にある他の行を使用する場合は、$inserts
を構築するためにもう少しメモリを使用します。 アレイ、はるかに効率的なバルクインサートと引き換えに。
insertBulkPrepared()
を書き換えることも可能です 多次元配列を受け入れて、潜在的なエラーの1つの原因を排除しますが、それは、配列をチャンク化した後に平坦化する必要があります。