Het is mogelijk om een bulkquery voor het invoegen van een instructie voor te bereiden door deze on-the-fly te construeren, maar er zijn een paar trucjes voor nodig. De belangrijkste bits gebruiken str_pad()
om een queryreeks van variabele lengte te maken en call_user_func_array()
te gebruiken om bind_param()
. aan te roepen met een variabel aantal parameters.
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;
}
Deze functie neemt uw $db
als een mysqli
instantie, een tabelnaam, een reeks veldnamen en een platte reeks verwijzingen naar waarden. Het voegt maximaal 500 records per zoekopdracht in, waarbij waar mogelijk voorbereide instructies worden hergebruikt. Het retourneert true
als alle invoegingen zijn gelukt, of false
als een van hen is mislukt. Waarschuwingen:
- De tabel- en veldnamen zijn niet voorzien van escapetekens; Ik laat het aan jou over om ervoor te zorgen dat ze geen backticks bevatten. Gelukkig mogen ze nooit afkomstig zijn van gebruikersinvoer.
- Als de lengte van
$values
is geen even veelvoud van de lengte van$fields
, zal het laatste stuk waarschijnlijk mislukken in de voorbereidingsfase. - Evenzo is de lengte van de
$types
parameter moet overeenkomen met de lengte van$fields
in de meeste gevallen, vooral wanneer sommige ervan verschillen. - Het maakt geen onderscheid tussen de drie manieren om te falen. Het houdt ook niet bij hoeveel invoegingen zijn gelukt, en het probeert ook niet door te gaan na een fout.
Als deze functie is gedefinieerd, kan uw voorbeeldcode worden vervangen door iets als:
$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>";
}
Die ampersands zijn belangrijk, omdat mysqli_stmt::bind_param
verwacht referenties, die niet worden geleverd door call_user_func_array
in recente versies van PHP.
U heeft ons niet de originele voorbereide verklaring gegeven, dus u moet waarschijnlijk de tabel- en veldnamen aanpassen. Het lijkt er ook op dat uw code zich in een lus bevindt over $i
; in dat geval alleen de for
lus moet zich in de buitenste lus bevinden. Als je de andere regels buiten de lus neemt, gebruik je wat meer geheugen bij het construeren van de $inserts
array, in ruil voor veel efficiëntere bulk-inserts.
Het is ook mogelijk om insertBulkPrepared()
. te herschrijven om een multidimensionale array te accepteren, waarbij één bron van mogelijke fouten wordt geëlimineerd, maar daarvoor moet de array worden afgevlakt nadat deze is opgedeeld.