オンザフライで作成して一括挿入ステートメント クエリを準備することは可能ですが、いくつかのコツが必要です。最も重要なビットは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>";
}
最近のバージョンの PHP ではmysqli_stmt::bind_param
提供されていない参照が期待されるため、これらのアンパサンドは重要です。call_user_func_array
元の準備済みステートメントが提供されていないため、おそらくテーブルとフィールドの名前を調整する必要があります。また、コードがループ内にあるように見えます$i
。その場合、ループのみがfor
外側のループ内にある必要があります。他の行をループの外に出すと、$inserts
より効率的な一括挿入と引き換えに、配列を構築するためにより多くのメモリを使用することになります。
insertBulkPrepared()
多次元配列を受け入れるように書き直して、潜在的なエラーの原因を 1 つ排除することもできますが、そのためには配列をチャンク化した後に配列を平坦化する必要があります。