Mysql
 sql >> Database >  >> RDS >> Mysql

MySQLi:inserimento di più righe con un'istruzione preparata

È possibile preparare una query di istruzioni di inserimento in blocco costruendola al volo, ma sono necessari alcuni trucchi. I bit più importanti stanno usando str_pad() per costruire una stringa di query di lunghezza variabile e utilizzando call_user_func_array() per chiamare bind_param() con un numero variabile di parametri.

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

Questa funzione prende il tuo $db come mysqli istanza, un nome di tabella, una matrice di nomi di campo e una matrice piatta di riferimenti a valori. Inserisce fino a 500 record per query, riutilizzando le istruzioni preparate quando possibile. Restituisce true se tutti gli inserimenti sono riusciti, o false se qualcuno di loro ha fallito. Avvertenze:

  • I nomi delle tabelle e dei campi non vengono sottoposti a escape; Lascio a te assicurarti che non contengano backtick. Fortunatamente, non dovrebbero mai provenire dall'input dell'utente.
  • Se la lunghezza di $values non è un multiplo pari della lunghezza di $fields , il blocco finale probabilmente non riuscirà nella fase di preparazione.
  • Allo stesso modo, la lunghezza dei $types il parametro deve corrispondere alla lunghezza di $fields nella maggior parte dei casi, in particolare quando alcuni di essi differiscono.
  • Non fa distinzione tra i tre modi per fallire. Inoltre, non tiene traccia di quanti inserimenti sono riusciti, né tenta di continuare dopo un errore.

Con questa funzione definita, il tuo codice di esempio può essere sostituito con qualcosa del tipo:

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

Queste e commerciali sono importanti, perché mysqli_stmt::bind_param si aspetta riferimenti, che non sono forniti da call_user_func_array nelle versioni recenti di PHP.

Non ci hai fornito la dichiarazione originale preparata, quindi probabilmente devi modificare i nomi di tabella e campo. Sembra anche che il tuo codice si trovi all'interno di un ciclo su $i; in tal caso, solo il for il ciclo deve essere all'interno del ciclo esterno. Se porti le altre righe al di fuori del ciclo, utilizzerai un po' più di memoria per costruire $inserts array, in cambio di inserimenti di massa molto più efficienti.

È anche possibile riscrivere insertBulkPrepared() per accettare un array multidimensionale, eliminando una fonte di potenziale errore, ma ciò richiede l'appiattimento dell'array dopo averlo spezzettato.