Mysql
 sql >> Datenbank >  >> RDS >> Mysql

MySQLi :Einfügen mehrerer Zeilen mit einer vorbereiteten Anweisung

Es ist möglich, eine Bulk-Insert-Anweisungsabfrage vorzubereiten, indem man sie spontan erstellt, aber es braucht ein paar Tricks. Die wichtigsten Bits sind die Verwendung von str_pad() um eine Abfragezeichenfolge variabler Länge zu erstellen, und mit call_user_func_array() um bind_param() aufzurufen mit einer variablen Anzahl von Parametern.

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

Diese Funktion nimmt Ihre $db als mysqli Instanz, ein Tabellenname, ein Array von Feldnamen und ein flaches Array von Verweisen auf Werte. Es fügt bis zu 500 Datensätze pro Abfrage ein und verwendet vorbereitete Anweisungen, wenn möglich, wieder. Es gibt true zurück wenn alle Einfügungen erfolgreich waren, oder false wenn einer von ihnen fehlgeschlagen ist. Vorbehalte:

  • Die Tabellen- und Feldnamen werden nicht maskiert; Ich überlasse es Ihnen, sicherzustellen, dass sie keine Backticks enthalten. Glücklicherweise sollten sie niemals von Benutzereingaben stammen.
  • Ist die Länge von $values ist kein gerades Vielfaches der Länge von $fields , der letzte Chunk wird wahrscheinlich in der Vorbereitungsphase scheitern.
  • Ebenso die Länge der $types Parameter sollte der Länge von $fields entsprechen in den meisten Fällen, besonders wenn sich einige von ihnen unterscheiden.
  • Es wird nicht zwischen den drei Arten des Scheiterns unterschieden. Es verfolgt auch nicht, wie viele Einfügungen erfolgreich waren, und versucht auch nicht, nach einem Fehler fortzufahren.

Wenn diese Funktion definiert ist, kann Ihr Beispielcode durch Folgendes ersetzt werden:

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

Diese kaufmännischen Und-Zeichen sind wichtig, weil mysqli_stmt::bind_param erwartet Referenzen, die nicht von call_user_func_array bereitgestellt werden in neueren Versionen von PHP.

Sie haben uns nicht die ursprüngliche vorbereitete Anweisung gegeben, daher müssen Sie wahrscheinlich die Tabellen- und Feldnamen anpassen. Es sieht auch so aus, als ob Ihr Code in einer Schleife über $i sitzt; in diesem Fall nur der for Schleife muss sich innerhalb der äußeren Schleife befinden. Wenn Sie die anderen Zeilen außerhalb der Schleife nehmen, benötigen Sie etwas mehr Speicher, um die $inserts zu erstellen -Array, im Gegenzug für viel effizientere Masseneinfügungen.

Es ist auch möglich, insertBulkPrepared() umzuschreiben um ein mehrdimensionales Array zu akzeptieren, wodurch eine potenzielle Fehlerquelle eliminiert wird, aber das erfordert, dass das Array nach dem Aufteilen in Blöcke reduziert wird.