Tengo una matriz como esta:
$array = ['1' , '2' ,'100'];
y mi consulta es:
$query = Customer::whereIn('id', $array)->get();
Cuando $array
es demasiado grande (1500 elementos y más), no funciona y devuelve un valor nulo.
También aumenté el max_allowed_packet
en la configuración de MySQL. pero problema no resuelto.
PHP v7.33, laravel v7.19, MySQL v5.7
Ok, esto me tomó 6 horas seguidas, ¡pero logré encontrar una explicación profunda del problema!
De hecho, el problema no es con elocuente en sí mismo, sino con la combinación de elocuente y la base de datos para optimizar las consultas.
Eloquent prepara todas las consultas con PDO::prepare()
. En el caso de una consulta whereIn(), el resultado es algo así como
PREPARE SELECT * FROM model WHERE id IN (?, ?, ?, ?, ?, ?, ?, ?, ?, .....);
Por su parte, la base de datos (mySQL, mariaDB) tiene una variable llamada in_predicate_conversion_threshold
para optimizar específicamente la cláusula IN(). De forma predeterminada, el valor es 1000
, lo que significa que la consulta se convertirá a una versión optimizada que implica subconsultas.
Por lo tanto, vincular más de 999 elementos en una cláusula IN puede generar errores en los que la base de datos devolverá 0 resultados sin errores.
Solución alternativa 1 : use consultas sin formato en el lado de laravel.
Solución alternativa 2 : use declaraciones no preparadas.
Solución alternativa 3 : haga fragmentos para selecciones o actualizaciones.
Solución alternativa 4 : aumente o elimine el umbral para la optimización IN (valor de 0 = sin optimización).
Para el último punto, simplemente ejecute SET in_predicate_conversion_threshold = 0;
antes de su consulta, o configúrelo globalmente en el archivo de configuración. (no ejecute la consulta directamente en la línea de comando ya que se trata de modificaciones basadas en sesiones)
Me gustaría poner un UP sobre este tema porque también lo encontramos en nuestra infraestructura.
El mayor problema no es que Eloquent devuelva nulo o vacío, sino el hecho de que no produce ningún error , por lo que es muy difícil de depurar.
Use whereIntegerInRaw, que se asegura de que la matriz sea solo números enteros, pero omite la preparación
Customer::whereIntegerInRaw('id', $array)->get();