php - sentencias - prepared query pdo
php PDO insertar lote múltiples filas con marcadores de posición (5)
Estoy buscando hacer varias inserciones usando PHP DOP.
La respuesta más cercana que he encontrado es esta
how-to-insert-an-array-into-a-single-mysql-prepared-statement
Sin embargo, el ejemplo que se ha dado utiliza? en lugar de marcadores de posición reales.
He mirado los ejemplos en el sitio de documentos PHP para los titulares de lugar
php.net pdo.prepared-sentencias
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
$stmt->bindParam('':name'', $name);
$stmt->bindParam('':value'', $value);
Ahora digamos que quería lograr lo anterior pero con una matriz
$valuesToInsert = array(
0 => array(''name'' => ''Robert'', ''value'' => ''some value''),
1 => array(''name'' -> ''Louise'', ''value'' => ''another value'')
);
¿Cómo lo haría con DOP y múltiples inserciones por transacción?
Me imagino que comenzaría con un bucle?
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
foreach($valuesToInsert as $insertRow){
// now loop through each inner array to match binded values
foreach($insertRow as $column => value){
$stmt->bindParam(":{$column}", value);
}
}
$stmt->execute();
Sin embargo, lo anterior no funciona, pero esperamos que demuestre lo que estoy tratando de lograr
En primer lugar ?
los símbolos son marcadores de posición reales (la mayoría de los controladores permiten utilizar sintaxis, posicionales y lugares con nombre). En segundo lugar, las declaraciones preparadas no son más que una herramienta para inyectar información sin procesar en las declaraciones de SQL: la sintaxis de la declaración de SQL en sí no se ve afectada. Ya tienes todos los elementos que necesitas:
- Cómo insertar múltiples filas con una sola consulta
- Cómo generar SQL dinámicamente
- Cómo usar declaraciones preparadas con los poseedores de lugares nombrados.
Es bastante trivial combinarlos todos:
$sql = ''INSERT INTO table (memberID, programID) VALUES '';
$insertQuery = array();
$insertData = array();
$n = 0;
foreach ($data as $row) {
$insertQuery[] = ''(:memberID'' . $n . '', :programID'' . $n . '')'';
$insertData[''memberID'' . $n] = $memberid;
$insertData[''programID'' . $n] = $row;
$n++;
}
if (!empty($insertQuery)) {
$sql .= implode('', '', $insertQuery);
$stmt = $db->prepare($sql);
$stmt->execute($insertData);
}
Mover ejecutar dentro del bucle.
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
foreach($valuesToInsert as $insertRow)
{
$stmt->execute($insertRow);
}
Si experimenta algún problema con esta forma recomendada, tiene que hacer una pregunta, describiendo estos ciertos problemas.
Su código estaba realmente bien, pero tenía un problema en $stmt->bindParam(":$column", value);
Debe ser $stmt->bindValue(":{$column}", $value);
y funcionará perfectamente. Esto ayudará a otros en el futuro.
Código completo:
foreach($params as $row)
{
// now loop through each inner array to match bound values
foreach($row as $column => $value)
{
$stmt->bindValue(":{$column}", $value); //EDIT
}
// Execute statement to add to transaction
$stmt->execute();
}
Supongo que está utilizando InnoDB, por lo que esta respuesta solo es válida para ese motor (o cualquier otro motor con capacidad de transacción, lo que significa que MyISAM no está incluido).
Por defecto, InnoDB se ejecuta en modo de confirmación automática. Eso significa que cada consulta se trata como su propia transacción contenida.
Para traducir eso a algo que nosotros, los mortales, podemos entender, significa que cada consulta INSERT que emita forzará la confirmación del disco duro confirmando que anotó la información de la consulta. Teniendo en cuenta que los discos duros mecánicos son súper lentos, ya que su operación de entrada-salida por segundo es baja (si no me equivoco, el promedio es de 300 IO), significa que sus 50 000 consultas serán: bueno, súper lentas.
Entonces, ¿Qué haces? Usted confirma todas sus consultas de 50k en una sola transacción. Puede que no sea la mejor solución para varios propósitos, pero será rápido.
Lo haces así:
$dbh->beginTransaction();
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
foreach($valuesToInsert as $insertRow)
{
// now loop through each inner array to match bound values
foreach($insertRow as $column => value)
{
$stmt->bindParam(":$column", value);
$stmt->execute();
}
}
$dbh->commit();
Unas pequeñas modificaciones en la solución proporcionadas por NB.
$ stmt-> execute () debe estar fuera del bucle interno porque puede tener una o más columnas que deben vincularse antes de llamar a $ stmt-> execute (); de lo contrario, obtendrá una excepción "Número de parámetro no válido: el número de variables vinculadas no coincide con el número de token ".
Segunda variable de "valor" faltaban signos de dólar.
function batchinsert($sql,$params){
try {
db->beginTransaction();
$stmt = db->prepare($sql);
foreach($params as $row)
{
// now loop through each inner array to match bound values
foreach($row as $column => $value)
{
$stmt->bindParam(":$column", $value);
}
$stmt->execute();
}
db->commit();
} catch(PDOExecption $e) {
$db->rollback();
}
}
Prueba:
$sql = "INSERT INTO `test`(`name`, `value`) VALUES (:name, :value)" ;
$data = array();
array_push($data, array(''name''=>''Name1'',''value''=>''Value1''));
array_push($data, array(''name''=>''Name2'',''value''=>''Value2''));
array_push($data, array(''name''=>''Name3'',''value''=>''Value3''));
array_push($data, array(''name''=>''Name4'',''value''=>''Value4''));
array_push($data, array(''name''=>''Name5'',''value''=>''Value5''));
batchinsert($sql,$data);