Je možné připravit dotaz hromadného vložení příkazem jeho vytvořením za běhu, ale vyžaduje to několik triků. Nejdůležitější bity jsou pomocí str_pad()
k vytvoření řetězce dotazu proměnné délky a pomocí call_user_func_array()
k volání bind_param()
s proměnným počtem parametrů.
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;
}
Tato funkce vám zabere $db
jako mysqli
instance, název tabulky, pole názvů polí a ploché pole odkazů na hodnoty. Do každého dotazu vloží až 500 záznamů a pokud je to možné, znovu použije připravené příkazy. Vrací true
pokud byly všechny vložení úspěšné, nebo false
pokud některý z nich selhal. Upozornění:
- Názvy tabulek a polí nejsou kódovány; Nechám na vás, abyste zajistili, že neobsahují zpětné zaškrtnutí. Naštěstí by nikdy neměly pocházet z uživatelského vstupu.
- Pokud je délka
$values
není sudým násobkem délky$fields
, poslední kus pravděpodobně selže ve fázi přípravy. - Stejně tak délka
$types
Parametr by měl odpovídat délce$fields
ve většině případů, zvláště když se některé z nich liší. - Nerozlišuje mezi třemi způsoby selhání. Také nesleduje, kolik vložení bylo úspěšné, ani se nepokouší pokračovat po chybě.
S touto funkcí definovanou může být váš ukázkový kód nahrazen něčím jako:
$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>";
}
Tyto ampersandy jsou důležité, protože mysqli_stmt::bind_param
očekává reference, které neposkytuje call_user_func_array
v nejnovějších verzích PHP.
Nedali jste nám původní připravený výpis, takže pravděpodobně budete muset upravit názvy tabulek a polí. Zdá se také, že váš kód je umístěn ve smyčce přes $i
; v tom případě pouze for
smyčka musí být uvnitř vnější smyčky. Pokud vezmete ostatní řádky mimo smyčku, spotřebujete o něco více paměti pro vytvoření $inserts
pole, výměnou za mnohem efektivnější hromadné vložky.
Je také možné přepsat insertBulkPrepared()
přijmout vícerozměrné pole, čímž se eliminuje jeden zdroj potenciálních chyb, ale to vyžaduje zploštění pole po jeho rozdělení.