sirve - limitar numero de registros en consulta mysql
¿Por qué Rails está agregando `OR 1=0` a las consultas que usan la sintaxis hash de la cláusula where con un rango? (5)
El proyecto en el que estoy trabajando es usar MySQL en RDS (mysql2 gema específicamente).
Cuando uso un hash de condiciones, incluido un rango en una declaración where
, obtengo un poco de una extraña adición a mi consulta.
User.where(id: [1..5])
y
User.where(id: [1...5])
Resultado en las siguientes consultas respectivamente:
SELECT `users`.* FROM `users` WHERE ((`users`.`id` BETWEEN 1 AND 5 OR 1=0))
SELECT `users`.* FROM `users` WHERE ((`users`.`id` >= 1 AND `users`.`id` < 5 OR 1=0))
Las consultas funcionan perfectamente bien ya que OR FALSE
es, efectivamente, un no-op. Me pregunto por qué Rails o ARel están agregando este fragmento en la consulta.
EDITAR
Parece que la línea que podría explicar esto es la línea 26 en ActiveRecord::PredicateBuilder
. ¿Todavía no tienes idea de cómo el hash podría estar empty?
En ese punto, pero tal vez alguien más lo hace.
Editar 2
Esto es interesante. Estaba estudiando el comentario de Filip para ver por qué lo hizo, ya que parece una aclaración, pero tiene razón en que 1..5 != [1..5]
. El primero es un rango inclusivo de 1 a 5, mientras que el último es una matriz cuyo primer elemento es el primero. ¡Intenté poner estos en un ARel where
llama para ver el SQL producido y el OR 1=0
no está allí!
User.where(id: 1..5) #=> SELECT "users".* FROM "users" WHERE ("users"."id" BETWEEN 1 AND 5)
User.where(id: 1...5) #=> SELECT "users".* FROM "users" WHERE ("users"."id" >= 1 AND "users"."id" < 5)
Aunque todavía no sé por qué ARel está agregando el OR 1=0
que siempre será falso y aparentemente innecesario. Puede deberse a cómo Array
s y Range
s se manejan de manera diferente.
Compruebe si está utilizando active_record-acts_as. Ese fue el problema conmigo.
Agregue la siguiente línea a su Gemfile:
gem ''active_record-acts_as'', :git => ''https://github.com/hzamani/active_record-acts_as.git''
Esto solo tirará de la última versión de la gema que, con suerte, será corregida. Trabajó para mi.
Creo que estás viendo los efectos secundarios de rubí personalmente.
Creo que la mejor manera de hacer lo que estás haciendo sería con
2.0.0-p481@meri :008 > [*1..5]
=> [1, 2, 3, 4, 5]
User.where(id: [*1..5]).to_sql
"SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 2, 3, 4, 5)"
Como esto crea una matriz frente a una matriz con el elemento 1 de rango de clase.
O
use un rango explícito para activar el BETWEEN en AREL.
# with end element, i.e. exclude_end=false
2.0.0-p481@meri :013 > User.where(id: Range.new(1,5)).to_sql
=> "SELECT `users`.* FROM `users` WHERE (`users`.`id` BETWEEN 1 AND 5)"
# without end element, i.e. exclude_end=true
2.0.0-p481@meri :022 > User.where(id: Range.new(1, 5, true)).to_sql
=> "SELECT `users`.* FROM `users` WHERE (`users`.`id` >= 1 AND `users`.`id` < 5)"
Esto es estrictamente una suposición, ya que hice algo similar en un proyecto propio (aunque utilicé AND 1
).
Por la razón que sea, al generar una consulta, es más fácil tener siempre una cláusula WHERE
que contenga un no-op que generar condicionalmente la cláusula WHERE
. Es decir, si no incluye ninguna sección where
terminará, se generará algo que todavía es válido.
Por otro lado, no estoy seguro de por qué está tomando esta forma: cuando lo hice, uso 1 [<AND (generated code)>...]
permitió el encadenamiento arbitrario, pero no veo cómo lo hace. Re viendo lo permitiría. Sin embargo, todavía creo que es probable que sea el resultado de un esquema de generación de código algorítmico.
Si le interesa tener el control de las consultas que genera y toda la potencia del lenguaje SQL y las características de la base de datos, le sugiero que se cambie de ActiveRecord / Arel a Sequel.
Honestamente, puedo decir que hay muchas más peculiaridades y tiempos de angustia por delante con ActiveRecord, especialmente cuando te mueves más allá de las simples consultas crudiales. Cuando empiece a intentar consultar sus datos con ira, tal vez necesite unir algunas tablas de combinación aquí y allá y darse cuenta de que realmente necesita condiciones de unión o una unión de consultas de todo tipo.
También es significativamente más rápido y más confiable en su generación de consultas y manejo de resultados y mucho más fácil de redactar las consultas que desee. También tiene documentación real que realmente puedes leer a diferencia de arel.
Solo desearía haberlo descubierto mucho antes en lugar de persistir con la capa de acceso a datos predeterminada de los rieles.
Sobre la base del hecho, que ha descubierto, que [1..5]
no es la forma correcta de especificar el rango ... He descubierto por qué [1..5]
comporta como lo hace. Para llegar allí, primero encontré que una matriz vacía en una condición de hash produce la condición de SQL 1=0
:
User.where(id: []).to_sql
# => "SELECT /"users/".* FROM /"users/" WHERE 1=0"
Y, si verifica el código ActiveRecord :: PredicateBuilder :: ArrayHandler , verá que los valores de la matriz siempre se dividen en rangos y otros valores.
ranges, values = values.partition { |v| v.is_a?(Range) }
Esto explica por qué no ve 1=0
cuando se usan valores sin rango. Es decir, la única forma de obtener 1=0
de una matriz sin incluir un rango es proporcionar una matriz vacía, que produce la condición 1=0
, como se muestra arriba. Y cuando toda la matriz tiene un rango, se van a obtener las condiciones del rango ( ranges
) y, por separado, se ejecuta una condición de la matriz vacía ( values
). Mi conjetura es que no hay una buena razón para esto ... simplemente es más fácil dejar que esto sea que evitarlo (ya que el conjunto de resultados es equivalente en ambos sentidos). Si el código de la partición era un poco más inteligente, no tendría que añadir la matriz de values
vacía adicional y podría omitir la condición 1=0
.
En cuanto a de dónde viene el 1=0
en primer lugar ... Creo que viene del adaptador de base de datos, pero no pude encontrar exactamente dónde. Sin embargo, lo llamaría un intento de no encontrar un registro. En otras palabras, WHERE 1=0
nunca devolverá a ningún usuario, lo que tiene sentido sobre el SQL alternativo como WHERE id=null
que encontrará a cualquier usuario cuyo id sea nulo (al darse cuenta de que esto no es realmente una sintaxis SQL correcta) . Y esto es lo que esperaría al intentar encontrar a todos los Usuarios cuyo id esté en el conjunto vacío (es decir, no estamos pidiendo nulos ids o null ids o lo que sea). Por lo tanto, en mi opinión, dejar de lado exactamente de dónde viene 1=0
como una caja negra está bien. ¡Al menos ahora podemos razonar sobre por qué el rango dentro de la matriz está causando que se muestre!
ACTUALIZAR
También encontré que, incluso cuando usas ARel directamente, puedes obtener 1=0
:
User.arel_table[:id].in([]).to_sql
# => "1=0"