Rails: uso mayor que / menor que con una instrucción where

142

Estoy tratando de encontrar a todos los usuarios con una identificación superior a 200, pero tengo algunos problemas con la sintaxis específica.

User.where(:id > 200) 

y

User.where("? > 200", :id) 

Ambos han fallado.

¿Alguna sugerencia?

Adam Templeton
fuente

Respuestas:

276

Prueba esto

User.where("id > ?", 200) 
RadBrad
fuente
1
También echa un vistazo a la gema Squeel de Ernie Miller
cpuguy83
77
¿Hay alguna razón para preferir usar ?, en lugar de incluir el 200?
davetapley
20
escapa automáticamente a los 200 (si el usuario pudiera ingresar el valor, evita la posibilidad de ataques de inyección SQL)
user1051849
124

Solo he probado esto en Rails 4, pero hay una forma interesante de usar un rango con un wherehash para obtener este comportamiento.

User.where(id: 201..Float::INFINITY)

generará el SQL

SELECT `users`.* FROM `users`  WHERE (`users`.`id` >= 201)

Lo mismo se puede hacer por menos que con -Float::INFINITY.

Acabo de publicar una pregunta similar sobre cómo hacer esto con fechas aquí en SO .

>= vs >

Para evitar que las personas tengan que profundizar y seguir la conversación de comentarios, aquí están los aspectos más destacados.

El método anterior solo genera una >=consulta y no a >. Hay muchas formas de manejar esta alternativa.

Para números discretos

Puede utilizar una number_you_want + 1estrategia como la anterior en la que estoy interesado en Usuarios con los que id > 200realmente busco id >= 201. Esto está bien para números enteros y números donde puede incrementar en una sola unidad de interés.

Si tiene el número extraído en una constante bien nombrada, este puede ser el más fácil de leer y comprender de un vistazo.

Lógica invertida

Podemos usar el hecho de que x > y == !(x <= y)y usar la cadena where not.

User.where.not(id: -Float::INFINITY..200)

que genera el SQL

SELECT `users`.* FROM `users` WHERE (NOT (`users`.`id` <= 200))

Esto toma un segundo extra para leer y razonar, pero funcionará para valores no discretos o columnas donde no puede usar el + 1 estrategia.

Mesa Arel

Si quieres ponerte elegante, puedes usar el Arel::Table.

User.where(User.arel_table[:id].gt(200))

generará el SQL

"SELECT `users`.* FROM `users` WHERE (`users`.`id` > 200)"

Los detalles son los siguientes:

User.arel_table              #=> an Arel::Table instance for the User model / users table
User.arel_table[:id]         #=> an Arel::Attributes::Attribute for the id column
User.arel_table[:id].gt(200) #=> an Arel::Nodes::GreaterThan which can be passed to `where`

Este enfoque le dará el SQL exacto que le interesa, sin embargo, no muchas personas usan la tabla Arel directamente y pueden encontrarla desordenada y / o confusa. Usted y su equipo sabrán qué es lo mejor para usted.

Prima

¡A partir de Rails 5 también puedes hacerlo con las fechas!

User.where(created_at: 3.days.ago..DateTime::Infinity.new)

generará el SQL

SELECT `users`.* FROM `users` WHERE (`users`.`created_at` >= '2018-07-07 17:00:51')

Doble bonificación

Una vez que se lance Ruby 2.6 (25 de diciembre de 2018), ¡podrá usar la nueva sintaxis de rango infinito! En lugar de 201..Float::INFINITYsolo podrás escribir 201... Más información en esta publicación de blog .

Aaron
fuente
3
¿Por qué es esto superior a la respuesta aceptada, por curiosidad?
mecampbellsoup
55
Superior es engañoso. En general, obtiene más flexibilidad con sus consultas ARel si puede utilizar la sintaxis hash sobre las cadenas, por lo que muchos preferirían esta solución. Dependiendo de su proyecto / equipo / organización, es posible que desee algo que sea más fácil para alguien que eche un vistazo al código, que es la respuesta aceptada.
Aaron
2
No creo que puedas hacer eso usando los wherecomparadores básicos . Pues >sugiero usar un >= (number_you_want + 1)para simplificar. Si realmente quiere asegurarse de que es solo una >consulta, puede acceder a la tabla ARel. Cada clase que hereda ActiveRecordtiene un arel_tablemétodo getter que devuelve el Arel::Tablepara esa clase. Se accede a las columnas de la tabla con el []método like User.arel_table[:id]. Esto devuelve un Arel::Attributes::Attributeque puede llamar gty pasar 200. Esto se puede pasar a where. por ej User.where(User.arel_table[:id].gt(200)).
Aaron
2
@bluehallu, ¿puedes dar un ejemplo? Lo siguiente no me funciona User.where(created_at: 3.days.ago..DateTime::Infinity.new).
Aaron
1
Ah! Es probable que haya un nuevo cambio en Rails 5 que le está dando el comportamiento beneficioso. En Rails 4.2.5 (Ruby 2.2.2) obtendrá una consulta con WHERE (users.created_at >= '2016-04-09 14:31:15' AND users.created_at < #<Date::Infinity:0x00>)(se omiten los ticks posteriores alrededor de los nombres de tabla y columna para el formato de comentario SO).
Aaron
22

Un mejor uso es crear un alcance en el modelo de usuario where(arel_table[:id].gt(id))

Mihai
fuente
6

Si desea una escritura más intuitiva, existe una gema llamada Squeel que le permitirá escribir sus instrucciones de esta manera:

User.where{id > 200}

Observe que los caracteres de 'llave' {} idson solo un texto.

Todo lo que tienes que hacer es agregar chirrido a tu Gemfile:

gem "squeel"

Esto podría facilitarle la vida al escribir sentencias SQL complejas en Ruby.

Douglas
fuente
11
Recomiendo evitar el uso de chirrido. A largo plazo es difícil de mantener y a veces tiene un comportamiento extraño. También tiene errores con ciertas versiones de Active Record
John Owen Chile
He estado usando Squeel durante algunos años y todavía estoy contento con él. Pero tal vez valga la pena probar otro ORM, como la secuela (<> squeel) por ejemplo, que parece prometedor características agradables para reemplazar ActiveRecord.
Douglas
5

Arel es tu amiga.

User.where (User.arel_table [: id] .gt (200))

joegiralt
fuente
4

Otra posibilidad elegante es ...

User.where("id > :id", id: 100)

Esta característica le permite crear consultas más comprensibles si desea reemplazar en varios lugares, por ejemplo ...

User.where("id > :id OR number > :number AND employee_id = :employee", id: 100, number: 102, employee: 1205)

Esto tiene más significado que tener mucho ?en la consulta ...

User.where("id > ? OR number > ? AND employee_id = ?", 100, 102, 1205)
Víctor
fuente
3

A menudo tengo este problema con los campos de fecha (donde los operadores de comparación son muy comunes).

Para dar más detalles sobre la respuesta de Mihai, que creo que es un enfoque sólido.

A los modelos puede agregar ámbitos como este:

scope :updated_at_less_than, -> (date_param) { 
  where(arel_table[:updated_at].lt(date_param)) }

... y luego en su controlador, o donde sea que esté usando su modelo:

result = MyModel.updated_at_less_than('01/01/2017')

... un ejemplo más complejo con combinaciones se ve así:

result = MyParentModel.joins(:my_model).
  merge(MyModel.updated_at_less_than('01/01/2017'))

Una gran ventaja de este enfoque es que (a) le permite componer sus consultas desde diferentes ámbitos y (b) evita las colisiones de alias cuando se une a la misma tabla dos veces, ya que arel_table manejará esa parte de la generación de consultas.

Brett Green
fuente
1

Carriles 6.1+

Rails 6.1 agregó una nueva 'sintaxis' para operadores de comparación en wherecondiciones, por ejemplo:

Post.where('id >': 9)
Post.where('id >=': 9)
Post.where('id <': 3)
Post.where('id <=': 3)

Entonces su consulta puede reescribirse de la siguiente manera:

User.where('id >': 200) 

Aquí hay un enlace a relaciones públicas donde puede encontrar más ejemplos.

Marian13
fuente
-3

Corta:

User.where("id > 200")
Bengala
fuente
8
Supongo que esto tiene que ser dinámico, y que el póster quiere evitar la inyección de SQL mediante el uso de consultas parametrizadas ( where("id > ?", 200)sintaxis). Esto no logra eso.
Matthew Hinea