sql >> Databasteknik >  >> RDS >> Mysql

MySQLi :Infoga flera rader med en förberedd sats

Det är möjligt att förbereda en bulk insert-satsförfrågan genom att konstruera den direkt, men det tar några knep. De viktigaste bitarna använder str_pad() för att konstruera en frågesträng med variabel längd och använda call_user_func_array() för att anropa bind_param() med ett variabelt antal parametrar.

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;
}

Den här funktionen tar din $db som en mysqli instans, ett tabellnamn, en array av fältnamn och en platt array av referenser till värden. Den infogar upp till 500 poster per fråga och återanvänder förberedda uttalanden när det är möjligt. Den returnerar true om alla insättningar lyckades, eller false om någon av dem misslyckades. Varningar:

  • Tabell- och fältnamnen är inte kodade; Jag låter det vara upp till dig att se till att de inte innehåller backticks. Lyckligtvis bör de aldrig komma från användarinput.
  • Om längden på $values är inte en jämn multipel av längden på $fields , kommer den sista biten förmodligen att misslyckas vid förberedelsestadiet.
  • På samma sätt, längden på $types parametern ska matcha längden på $fields i de flesta fall, särskilt när några av dem skiljer sig åt.
  • Den skiljer inte mellan de tre sätten att misslyckas. Den håller inte heller reda på hur många insättningar som lyckades, och försöker inte fortsätta efter ett fel.

Med den här funktionen definierad kan din exempelkod ersättas med något i stil med:

$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>";
}

Dessa et-tecken är viktiga eftersom mysqli_stmt::bind_param förväntar sig referenser, som inte tillhandahålls av call_user_func_array i de senaste versionerna av PHP.

Du gav oss inte det ursprungliga förberedda uttalandet, så du måste förmodligen justera tabell- och fältnamnen. Det ser också ut som att din kod sitter i en slinga över $i; i så fall endast for öglan måste vara inuti den yttre öglan. Om du tar de andra raderna utanför loopen kommer du att använda lite mer minne för att konstruera $inserts array, i utbyte mot mycket effektivare bulkskär.

Det är också möjligt att skriva om insertBulkPrepared() att acceptera en flerdimensionell array, vilket eliminerar en källa till potentiella fel, men det kräver att arrayen plattas till efter att den har formats i bitar.




  1. SQL Server-markörtyper - Vad är statiska markörer i SQL Server | SQL Server Tutorial / TSQL Tutorial

  2. Hur man tar bort posterna baserat på föregående och nästa rader och tilldelar datum baserat på vissa villkor

  3. Återställ en SQLite-databas

  4. UnicodeEncodeError:'latin-1' codec kan inte koda tecken