Rotaie: utilizzo maggiore di / minore di con un'istruzione where


142

Sto cercando di trovare tutti gli utenti con un ID maggiore di 200, ma ho qualche problema con la sintassi specifica.

User.where(:id > 200) 

e

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

hanno fallito entrambi.

Eventuali suggerimenti?

Risposte:


276

Prova questo

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

1
Dai un'occhiata anche alla gemma Squeel di Ernie Miller
cpuguy83 il

7
C'è qualche motivo per preferire l'uso ?, piuttosto che sottolineare 200?
davetapley,

20
sfugge automaticamente a 200 (se fosse possibile per l'utente inserire il valore, evita la possibilità di attacchi SQL injection)
user1051849

124

L'ho testato solo in Rails 4 ma esiste un modo interessante di utilizzare un intervallo con un wherehash per ottenere questo comportamento.

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

genererà l'SQL

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

Lo stesso può essere fatto per meno che con -Float::INFINITY.

Ho appena pubblicato una domanda simile chiedendo di farlo con le date qui su SO .

>= vs >

Per evitare che le persone debbano scavare e seguire i commenti, ecco i punti salienti.

Il metodo sopra genera solo una >=query e non a >. Esistono molti modi per gestire questa alternativa.

Per numeri discreti

È possibile utilizzare una number_you_want + 1strategia come sopra in cui sono interessato agli utenti id > 200ma in realtà cerco id >= 201. Questo va bene per numeri interi e numeri in cui è possibile incrementare di una singola unità di interesse.

Se il numero viene estratto in una costante ben denominata, questa può essere la più facile da leggere e comprendere a colpo d'occhio.

Logica invertita

Possiamo usare il fatto che x > y == !(x <= y)e usare la catena dove non.

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

che genera l'SQL

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

Questo richiede un secondo in più per leggere e ragionare, ma funzionerà per valori o colonne non discreti in cui non è possibile utilizzare + 1 strategia.

Tavolo Arel

Se vuoi divertirti, puoi usare il Arel::Table.

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

genererà l'SQL

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

Le specifiche sono le seguenti:

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`

Questo approccio ti darà l' esatto SQL a cui sei interessato, tuttavia non molte persone usano direttamente la tabella Arel e possono trovarla confusa e / o confusa. Tu e il tuo team saprete cosa è meglio per voi.

indennità

A partire da Rails 5 puoi farlo anche con le date!

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

genererà l'SQL

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

Doppio bonus

Una volta rilasciato Ruby 2.6 (25 dicembre 2018) sarai in grado di utilizzare la nuova sintassi della gamma infinita! Invece di 201..Float::INFINITYpoter scrivere 201... Maggiori informazioni in questo post del blog .


3
Perché questo è superiore alla risposta accettata, per curiosità?
mecampbellsoup,

5
Il superiore è fuorviante. In generale, si ottiene una maggiore flessibilità con le query ARel se si è in grado di utilizzare la sintassi hash rispetto alle stringhe, motivo per cui molti preferirebbero questa soluzione. A seconda del tuo progetto / team / organizzazione, potresti desiderare qualcosa che sia più facile per qualcuno dare un'occhiata al codice per capire quale sia la risposta accettata.
Aaron,

2
Non credo che tu possa farlo usando gli whereabbinamenti di base . Perché >suggerisco di usare a >= (number_you_want + 1)per semplicità. Se vuoi davvero assicurarti che sia solo una >query puoi accedere alla tabella ARel. Ogni classe che eredita ActiveRecordha un arel_tablemetodo getter che restituisce il valore Arel::Tableper quella classe. Le colonne sulla tabella sono accessibili con il []metodo like User.arel_table[:id]. Questo restituisce un Arel::Attributes::Attributeche puoi chiamare gte trasmettere 200. Questo può essere quindi passato a where. es User.where(User.arel_table[:id].gt(200)).
Aaron,

2
@bluehallu puoi fornire un esempio? Quanto segue non funziona per me User.where(created_at: 3.days.ago..DateTime::Infinity.new).
Aaron,

1
Ah! È probabile che un nuovo cambiamento in Rails 5 ti dia il comportamento benefico. In Rails 4.2.5 (Ruby 2.2.2) otterrai una query con WHERE (users.created_at >= '2016-04-09 14:31:15' AND users.created_at < #<Date::Infinity:0x00>)(segni di spunta intorno ai nomi di tabelle e colonne omessi per la formattazione dei commenti SO).
Aaron,

22

Un utilizzo migliore consiste nel creare un ambito nel modello utente where(arel_table[:id].gt(id))


6

Se vuoi una scrittura più intuitiva, esiste una gemma chiamata squeel che ti permetterà di scrivere le tue istruzioni in questo modo:

User.where{id > 200}

Nota i caratteri "parentesi graffe" {} ed idè solo un testo.

Tutto quello che devi fare è aggiungere il tergipavimento al tuo Gemfile:

gem "squeel"

Ciò potrebbe semplificarti molto la vita quando scrivi un'istruzione SQL complessa in Ruby.


11
Consiglio di evitare l'uso del tergipavimento. A lungo termine è difficile da mantenere e talvolta ha comportamenti strani. Inoltre è difettoso con alcune versioni di Active Record
John Owen Chile,

Uso il tergipavimento da alcuni anni e ne sono ancora contento. Ma forse vale la pena provare un altro ORM, come ad esempio il sequel (<> squeel), che sembra promettente funzioni interessanti per sostituire ActiveRecord.
Douglas,

5

Arel è tuo amico.

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


4

Un'altra possibilità di fantasia è ...

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

Questa funzione consente di creare query più comprensibili se si desidera sostituire in più posizioni, ad esempio ...

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

Questo ha più significato che avere molto ?sulla query ...

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

3

Ho spesso questo problema con i campi data (dove gli operatori di confronto sono molto comuni).

Per approfondire ulteriormente la risposta di Mihai, che credo sia un approccio solido.

Ai modelli è possibile aggiungere ambiti come questo:

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

... e poi nel tuo controller, o ovunque tu stia usando il tuo modello:

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

... un esempio più complesso con join è simile al seguente:

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

Un enorme vantaggio di questo approccio è (a) che consente di comporre le query da ambiti diversi e (b) evita le collisioni alias quando si unisce alla stessa tabella due volte poiché arel_table gestirà quella parte della generazione della query.


2

Rotaie 6.1+

Rails 6.1 ha aggiunto una nuova "sintassi" per gli operatori di confronto in wherecondizioni, ad esempio:

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

Quindi la tua query può essere riscritta come segue:

User.where('id >': 200) 

Ecco un link a PR dove puoi trovare altri esempi.


-3

Più breve:

User.where("id > 200")

8
Presumo che questo debba essere dinamico e che il poster vuole evitare l'iniezione SQL usando query parametrizzate ( where("id > ?", 200)sintassi). Questo non ci riesce.
Matthew Hinea,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.