Możliwe jest przygotowanie zapytania z instrukcją zbiorczego wstawiania, konstruując je w locie, ale wymaga to kilku sztuczek. Najważniejsze bity używają str_pad()
do skonstruowania ciągu zapytania o zmiennej długości i przy użyciu call_user_func_array()
wywołać bind_param()
ze zmienną liczbą parametrów.
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;
}
Ta funkcja pobiera Twój $db
jako mysqli
na przykład nazwę tabeli, tablicę nazw pól i płaską tablicę odniesień do wartości. Wstawia do 500 rekordów na zapytanie, w miarę możliwości ponownie wykorzystując przygotowane instrukcje. Zwraca true
jeśli wszystkie wstawienia się powiodły, lub false
jeśli któryś z nich zawiódł. Zastrzeżenia:
- Nazwy tabel i pól nie mają znaczenia; Pozostawiam tobie, aby upewnić się, że nie zawierają kleszczy. Na szczęście nigdy nie powinny pochodzić z danych wejściowych użytkownika.
- Jeśli długość
$values
nie jest nawet wielokrotnością długości$fields
, ostatnia porcja prawdopodobnie nie powiedzie się na etapie przygotowań. - Podobnie, długość
$types
parametr powinien odpowiadać długości$fields
w większości przypadków, zwłaszcza gdy niektóre z nich się różnią. - Nie rozróżnia trzech sposobów na porażkę. Nie śledzi również liczby wstawek, które zakończyły się sukcesem, ani nie próbuje kontynuować po błędzie.
Po zdefiniowaniu tej funkcji przykładowy kod można zastąpić czymś takim jak:
$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>";
}
Te znaki ampersand są ważne, ponieważ mysqli_stmt::bind_param
oczekuje referencji, których nie dostarcza call_user_func_array
w najnowszych wersjach PHP.
Nie przekazałeś nam oryginalnego przygotowanego zestawienia, więc prawdopodobnie musisz dostosować nazwy tabel i pól. Wygląda również na to, że Twój kod znajduje się w pętli nad $i
; w takim przypadku tylko for
pętla musi znajdować się wewnątrz zewnętrznej pętli. Jeśli wyjmiesz inne linie poza pętlę, zużyjesz nieco więcej pamięci, konstruując $inserts
tablicy, w zamian za znacznie wydajniejsze wstawianie zbiorcze.
Możliwe jest również przepisanie insertBulkPrepared()
zaakceptować wielowymiarową tablicę, eliminując jedno źródło potencjalnych błędów, ale to wymaga spłaszczenia tablicy po jej podzieleniu na kawałki.