sql >> Base de Datos >  >> RDS >> Mysql

MySQLi:Insertar varias filas con una declaración preparada

Es posible preparar una consulta de instrucción de inserción masiva construyéndola sobre la marcha, pero requiere algunos trucos. Los bits más importantes están usando str_pad() para construir una cadena de consulta de longitud variable y usar call_user_func_array() para llamar a bind_param() con un número variable de parámetros.

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

Esta función toma su $db como mysqli ejemplo, un nombre de tabla, una matriz de nombres de campo y una matriz plana de referencias a valores. Inserta hasta 500 registros por consulta, reutilizando declaraciones preparadas cuando sea posible. Devuelve true si todas las inserciones tuvieron éxito, o false si alguno de ellos falla. Advertencias:

  • Los nombres de la tabla y el campo no se escapan; Te dejo a ti que te asegures de que no contengan acentos graves. Afortunadamente, nunca deben provenir de la entrada del usuario.
  • Si la longitud de $values no es un múltiplo par de la longitud de $fields , el trozo final probablemente fallará en la etapa de preparación.
  • Del mismo modo, la longitud de los $types el parámetro debe coincidir con la longitud de $fields en la mayoría de los casos, particularmente cuando algunos de ellos difieren.
  • No distingue entre las tres formas de fallar. Tampoco realiza un seguimiento de cuántas inserciones se realizaron correctamente, ni intenta continuar después de un error.

Con esta función definida, su código de ejemplo se puede reemplazar con algo como:

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

Esos símbolos de unión son importantes, porque mysqli_stmt::bind_param espera referencias, que no son proporcionadas por call_user_func_array en versiones recientes de PHP.

No nos proporcionó la declaración preparada original, por lo que probablemente necesite ajustar los nombres de la tabla y los campos. También parece que su código se encuentra dentro de un bucle sobre $i; en ese caso, solo el for el bucle debe estar dentro del bucle exterior. Si saca las otras líneas fuera del ciclo, usará un poco más de memoria para construir $inserts matriz, a cambio de inserciones masivas mucho más eficientes.

También es posible reescribir insertBulkPrepared() para aceptar una matriz multidimensional, eliminando una fuente de error potencial, pero eso requiere aplanar la matriz después de fragmentarla.