sql >> Databáze >  >> RDS >> Mysql

MySQLi :Vložení více řádků s jedním připraveným příkazem

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í.




  1. Cizí klíče + dědění tabulky v PostgreSQL?

  2. Neznámý sloupec {0} v klauzuli

  3. Jaký je nejrychlejší způsob importu velké zálohy databáze mysql?

  4. Jaký je správný způsob, jak počítat komentáře k článku, hity a hodnocení Líbí se v indexu článků?