Възможно е да подготвите групова заявка за оператор за вмъкване, като я конструирате в движение, но това изисква няколко трика. Най-важните битове използват 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()
за приемане на многоизмерен масив, елиминирайки един източник на потенциална грешка, но това изисква изравняване на масива след разделянето му.