可以通过动态构建来准备批量插入语句查询,但这需要一些技巧。最重要的位是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
- 它没有区分三种失败的方式。它也不会跟踪成功插入的次数,也不会在出现错误后尝试继续。
定义此函数后,您的示例代码可以替换为以下内容:
$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()
以接受多维数组,从而消除潜在错误的一个来源,但这需要在分块后将数组展平。