multiples - php mysqli execute query
¿Cómo identificar la consulta que causó el error usando mysqli_multi_query? (4)
En su ciclo do, agregue un contador, cada mysqli_next_result exitoso incrementa el contador. Una vez que mysqli_next_result devuelve falso, también sale el contador.
Usando un ejemplo de otro lugar en SO para captar mejor los errores de ''ocultamiento''. Si bien el código siguiente captará y devolverá un error, ¿es posible mejorar esto para informar para qué consulta se produjo el error?
Con el código a continuación, la salida es:
Columns: 18
Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''FRO inventory'' at line 1
Código que se está probando:
$query = "SELECT * FROM orders WHERE location = ''IN'' ORDER BY orderNum DESC LIMIT 20;";
$query .= "SELECT * FRO inventory"; // With error
$ord = array();
$invent = array();
if(mysqli_multi_query($link, $query)) {
do {
// fetch results
if($result = mysqli_store_result($link)) {
echo ''Columns: '' . mysqli_field_count($link) . "<br>";
while($row = mysqli_fetch_assoc($result)) {
if(count($row) > 17)
$orders[] = $row;
elseif(count($row) == 6)
$inv[] = $row;
}
}
if(!mysqli_more_results($link))
break;
if(!mysqli_next_result($link)) {
// report error
echo ''Error: '' . mysqli_error($link);
break;
}
} while(true);
mysqli_free_result($result);
}
Esto funciona para dos consultas.
Si el error está en el primero, la respuesta para la primera consulta a PHP es el mensaje de error, y para el segundo (que no se activará), un mensaje que chismee sobre el primero.
Si el error está en el segundo, se devuelve la primera respuesta y la segunda recibe el mensaje de error.
Estoy usando matrices asociativas y elementos de matriz negativos [0]. Esto agrega la clave [''Error''] solo si hay un error relevante.
Finalmente, no soy el mejor PHPer, así que depende de ti arreglar lo que es feo.
$query_nbr=0;
if (mysqli_multi_query($link,$query ))
{
do
{
unset($field_info) ; $field_info = array() ;
unset($field_names) ; $field_names = array() ;
unset($values) ; $values = array(array()) ;
if ($result = mysqli_store_result($link))
{
$query_nbr += 1 ;
$rows_found = mysqli_num_rows($result);
$fields_returned = mysqli_num_fields($result);
$field_info = mysqli_fetch_fields($result);
$field_nbr=0;
foreach ($field_info as $fieldstuff)
{ $field_nbr +=1 ;
$field_names[$field_nbr] = $fieldstuff->name ;
}
$now = date("D M j G:i:s T Y") ;
if ($query_nbr == 1)
{
$result_vector1 = array(''when''=>$now) ;
$result_vector1[''nrows'']=0;
$result_vector1[''nrows'']=$rows_found ;
$result_vector1[''nfields'']=$fields_returned ;
$result_vector1[''field_names'']=$field_names ;
}
else
{
$result_vector2 = array(''when2''=>$now) ;
$result_vector2[''nrows2'']=0;
$result_vector2[''nrows2'']=$rows_found ;
$result_vector2[''nfields2'']=$fields_returned ;
$result_vector2[''field_names2'']=$field_names ;
}
$row_nbr=0 ;
while ($row = mysqli_fetch_array($result, MYSQLI_BOTH))
{
$row_nbr++ ;
for ($field_nbr=1;$field_nbr<=$fields_returned;$field_nbr++)
{
$values[$row_nbr][$field_names[$field_nbr]] =$row[$field_nbr-1] ;
}
}
mysqli_free_result($result) ;
unset($values[0]) ;
if ($query_nbr == 1)
{$result_vector1[''values'']=$values ;}
else
{$result_vector2[''values2'']=$values ;}
} // EO if ($result = mysqli_store_result($link))
} while (mysqli_more_results($link) && mysqli_next_result($link)) ;
} // EO if (mysqli_multi_query($link,$query ))
else
{
// This will be true if the 1st query failed
if ($query_nbr == 0)
{
$result_vector1[''Error''] = "MySQL Error #: ".mysqli_errno($link).": ".mysqli_error($link) ;
$result_vector2[''Error''] = "MySQL Error in first query." ;
}
} // EO MySQL
// Here we only made it through once, on the 2nd query
if ( $query_nbr == 1 && $nqueries == 2 && empty( $result_vector2 ) )
{
$result_vector2[''Error''] = "MySQL Error #: ".mysqli_errno($link).": ".mysqli_error($link) ;
}
Para responder a mi propia pregunta y debido a que la documentación es deficiente, he aquí una solución que con suerte ayudará a otros. Lo que falta es una forma de detectar un error en la primera consulta. (Las acciones ocultas de myqsqli_multi_query son difíciles de entender).
Ahora verifique las entradas en $ err array.
$q[1] = "SELECT * FROM orders WHERE location = ''IN'' ORDER BY orderNum DESC LIMIT 20";
$q[2] = "SELECT * FROM inventory";
$ord = array();
$invent = array();
$err = array();
$c = 1;
if(mysqli_multi_query($link, implode('';'', $q))) {
do {
// fetch results
if($result = mysqli_use_result($link))
while($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
if(count($row) > 17)
$orders[] = $row;
elseif(count($row) == 6)
$inv[] = $row;
}
}
$c++;
if(!mysqli_more_results($link))
break;
if(!mysqli_next_result($link) || mysqli_errno($link)) {
// report error
$err[$c] = mysqli_error($link);
break;
}
} while(true);
mysqli_free_result($result);
}
else
$err[$c] = mysqli_error($link);
mysqli_close($link);
Este es un enfoque que no solo mejorará la calidad de sus mensajes de error, sino que también mejorará la forma en que maneja sus conjuntos de resultados.
$q["Orders"]="SELECT * FROM orders WHERE location = ''IN'' ORDER BY orderNum DESC LIMIT 20";
$q["Inventory"]="SELECT * FRO inventory";
if(!$link=mysqli_connect("host","user","pass","db")){
echo "Failed to connect to MySQL: ",mysqli_connect_error();
}elseif(mysqli_multi_query($link,implode('';'',$q))){
do{
$q_key=key($q); // current query''s key name (Orders or Inventory)
if($result=mysqli_store_result($link)){ // if a result set... SELECTs do
while($row=mysqli_fetch_assoc($result)){ // if one or more rows, iterate all
$rows[$q_key][]=$row;
}
mysqli_free_result($result);
echo "<div><pre>"; // <pre> is for easier array reading
var_export($rows[$q_key]);
echo "</pre></div>";
}
} while(next($q) && mysqli_more_results($link) && mysqli_next_result($link));
}
if($mysqli_error=mysqli_error($link)){ // check & declare variable in same step to avoid duplicate func call
echo "<div style=/"color:red;/">Query Key = ",key($q),", Query = ",current($q),", Syntax Error = $mysqli_error</div>";
}
Error en la primera consulta : si su primera consulta intenta acceder a una tabla que no existe en la base de datos nominada, por ejemplo: ordersXYZ
Array $rows
no existirá, no se producirá var_export()
y verá esta respuesta:
Clave de consulta = Pedidos, Consulta = SELECCIONAR * FROM ordersXYZ DONDE location = ''IN'' ORDER BY orderNum DESC LIMIT 20, Error de sintaxis = Tabla ''[someDB] .ordersXYZ'' no existe
Error en la segunda consulta : si su primera consulta es exitosa, pero su segunda consulta intenta acceder a una tabla inexistente como: inventory2
$rows["Orders"]
contendrá los datos de fila deseados y será var_export()
''ed, $row["Inventory"]
no existirá, y verá esta respuesta:
Clave de consulta = Inventario, Consulta = SELECCIONAR * FROM inventory2, Error de sintaxis = Tabla ''[someDB] .inventory2'' no existe
Sin errores : si ambas consultas están libres de errores, su matriz $rows
se llenará con los datos deseados y var_export()
''ed, y no habrá respuesta de error. Con los datos consultados guardados en $rows
, puede acceder a lo que desee desde $rows["Orders"]
y $rows["Inventory"]
.
Cosas a tener en cuenta:
Puede notar que estoy haciendo declaraciones de variables y verificaciones condicionales al mismo tiempo, esto hace que el código sea más SECO.
Como mi enfoque utiliza
implode()
con un punto y coma en la líneaelseif
, asegúrese de no agregar un punto y coma final a sus consultas.Este conjunto de consultas siempre devuelve un conjunto de resultados porque todas son consultas SELECT. Si tiene una colección mixta de consultas que
affect_rows
, puede encontrar información útil en este enlace ( https://.com/a/22469722/2943403 ) .mysqli_multi_query()
dejará de ejecutar consultas tan pronto como haya un error. Si espera detectar errores "todos", descubrirá que nunca hay más de uno.Escribir puntos de ruptura condicionales como en la pregunta y solución del PO no es aconsejable. Si bien los puntos de corte personalizados se pueden usar correctamente en otras circunstancias, para este caso los puntos de ruptura se deben ubicar dentro de la instrucción
while()
del bloquedo()
.Una consulta que devuelve cero filas no generará un mensaje de error; simplemente no creará subcampos en
$rows
porque no se ingresará el ciclowhile()
.Al usar la función
key()
, se puede evitar la condiciónif/elseif
del OP que cuenta las columnas en cada fila del conjunto de resultados. Esta es una práctica mejor porque ejecutar una condición en cada iteración puede llegar a ser costosa en algunos casos. Observe que el puntero de matriz se avanza dentro de$q
al final de cada iteracióndo()
. Esta es una técnica adicional que no encontrará en la página de manual de php; permite quekey()
funcione según lo previsto.Y, por supuesto, las líneas
<div><pre>var_export()...</pre></div>
se pueden eliminar de su código de trabajo, fue solo para demostración.Si va a ejecutar más consultas después de este bloque de código que reutiliza variables, asegúrese de borrar todas las variables usadas para que los datos residuales no interfieran. por ejemplo,
$mysqli_error=null; // clear errors
$mysqli_error=null; // clear errors
yreset($q); // reset array pointer
reset($q); // reset array pointer
.Preste atención a esta advertencia algo vaga según su propio criterio: http://php.net/manual/en/mysqli.use-result.php :
No se debe usar mysqli_use_result () si se realiza mucho procesamiento en el lado del cliente, ya que esto atará el servidor y evitará que otros subprocesos actualicen las tablas desde las cuales se están obteniendo los datos.
Por último y MÁS IMPORTANTE por razones de seguridad, no muestre consultas ni consulte públicamente la información de error: no quiere que gente siniestra vea este tipo de comentarios. Igualmente importante, siempre proteja sus consultas de los ataques de inyección. Si sus consultas incluyen datos proporcionados por el usuario, debe filtrar / desinfectar los datos hasta la muerte antes de usarlos en
mysqli_multi_query()
. De hecho, al tratar con los comentarios de los usuarios, mi muy fuerte recomendación es alejarme demysqli_multi_query()
y usar declaraciones preparadas o pdo para las interacciones de la base de datos para un mayor nivel de seguridad.