Schienen: Verwenden von größer als / kleiner als mit einer where-Anweisung

142

Ich versuche, alle Benutzer mit einer ID größer als 200 zu finden, habe jedoch Probleme mit der spezifischen Syntax.

User.where(:id > 200) 

und

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

haben beide versagt.

Irgendwelche Vorschläge?

Adam Templeton
quelle

Antworten:

276

Versuche dies

User.where("id > ?", 200) 
RadBrad
quelle
1
Schauen Sie sich auch Squeel Juwel von Ernie Miller
cpuguy83
7
Gibt es einen Grund, die Verwendung zu bevorzugen ?, anstatt die zu inlinieren 200?
Davetapley
20
es entgeht automatisch der 200 (
wenn
124

Ich habe dies nur in Rails 4 getestet, aber es gibt eine interessante Möglichkeit, einen Bereich mit einem whereHash zu verwenden, um dieses Verhalten zu erhalten.

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

generiert die SQL

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

Das gleiche kann für weniger als mit gemacht werden -Float::INFINITY.

Ich habe gerade eine ähnliche Frage mit Daten hier auf SO gestellt .

>= vs. >

Um zu vermeiden, dass die Leute die Kommentare durchgehen und den Kommentaren folgen müssen, sind hier die Highlights.

Die oben beschriebene Methode nur erzeugt eine >=Abfrage und nicht ein >. Es gibt viele Möglichkeiten, mit dieser Alternative umzugehen.

Für diskrete Zahlen

Sie können eine number_you_want + 1Strategie wie oben verwenden, bei der ich an Benutzern interessiert bin, die id > 200aber tatsächlich suchen id >= 201. Dies ist in Ordnung für Ganzzahlen und Zahlen, bei denen Sie um eine einzelne interessierende Einheit erhöhen können.

Wenn Sie die Zahl in eine gut benannte Konstante extrahieren lassen, ist dies möglicherweise auf einen Blick am einfachsten zu lesen und zu verstehen.

Invertierte Logik

Wir können die Tatsache nutzen, dass x > y == !(x <= y)und die wo nicht Kette verwenden.

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

das generiert die SQL

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

Das Lesen und Begründen dauert eine zusätzliche Sekunde, funktioniert jedoch für nicht diskrete Werte oder Spalten, bei denen Sie die + 1Strategie nicht verwenden können .

Arel Tisch

Wenn Sie Lust haben, können Sie die nutzen Arel::Table.

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

generiert die SQL

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

Die Einzelheiten sind wie folgt:

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`

Mit diesem Ansatz erhalten Sie genau das SQL, an dem Sie interessiert sind. Allerdings verwenden nicht viele Benutzer die Arel-Tabelle direkt und können sie als unübersichtlich und / oder verwirrend empfinden. Sie und Ihr Team wissen, was für Sie am besten ist.

Bonus

Ab Rails 5 können Sie dies auch mit Daten tun!

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

generiert die SQL

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

Doppelter Bonus

Sobald Ruby 2.6 veröffentlicht ist (25. Dezember 2018), können Sie die neue Syntax für unendliche Bereiche verwenden! Stattdessen können 201..Float::INFINITYSie einfach schreiben 201... Weitere Infos in diesem Blogbeitrag .

Aaron
quelle
3
Warum ist dies aus Neugier der akzeptierten Antwort überlegen?
Mecampbellsoup
5
Superior ist irreführend. Im Allgemeinen erzielen Sie mit Ihren ARel-Abfragen mehr Flexibilität, wenn Sie die Hash-Syntax gegenüber Zeichenfolgen verwenden können, weshalb viele diese Lösung bevorzugen würden. Abhängig von Ihrem Projekt / Team / Ihrer Organisation möchten Sie möglicherweise etwas, das für jemanden, der auf den Code schaut, einfacher ist, um herauszufinden, welche Antwort akzeptiert wird.
Aaron
2
Ich glaube nicht, dass Sie das mit den grundlegenden whereMatchern tun können . Denn >ich schlage vor, der >= (number_you_want + 1)Einfachheit halber ein zu verwenden. Wenn Sie wirklich sicherstellen möchten, dass es sich nur um eine >Abfrage handelt, können Sie auf die ARel-Tabelle zugreifen. Jede Klasse, die von erbt, ActiveRecordverfügt über eine arel_tableGetter-Methode, die die Arel::Tablefür diese Klasse zurückgibt . Auf Spalten in der Tabelle wird mit der []Methode like zugegriffen User.arel_table[:id]. Dies gibt eine zurück, die Arel::Attributes::AttributeSie anrufen gtund weitergeben können 200. Dies kann dann an übergeben werden where. zB User.where(User.arel_table[:id].gt(200)).
Aaron
2
@bluehallu kannst du ein Beispiel geben? Das Folgende funktioniert bei mir nicht User.where(created_at: 3.days.ago..DateTime::Infinity.new).
Aaron
1
Ah! Es ist wahrscheinlich eine neue Änderung in Rails 5, die Ihnen das vorteilhafte Verhalten verleiht. In Rails 4.2.5 (Ruby 2.2.2) erhalten Sie eine Abfrage mit WHERE (users.created_at >= '2016-04-09 14:31:15' AND users.created_at < #<Date::Infinity:0x00>)(Back Ticks um Tabellen- und Spaltennamen, die für die SO-Kommentarformatierung weggelassen wurden).
Aaron
22

Eine bessere Verwendung besteht darin, einen Bereich im Benutzermodell zu erstellen where(arel_table[:id].gt(id))

Mihai
quelle
6

Wenn Sie intuitiver schreiben möchten, gibt es einen Edelstein namens Squeel , mit dem Sie Ihre Anweisungen wie folgt schreiben können:

User.where{id > 200}

Beachten Sie, dass die 'Klammer'-Zeichen {} idnur ein Text sind.

Alles, was Sie tun müssen, ist, Ihrem Gemfile Squeel hinzuzufügen:

gem "squeel"

Dies kann Ihr Leben beim Schreiben komplexer SQL-Anweisungen in Ruby erheblich erleichtern.

Douglas
quelle
11
Ich empfehle, Squeel zu vermeiden. Langfristig ist schwer zu pflegen und hat manchmal seltsames Verhalten. Es ist auch fehlerhaft mit bestimmten Active Record-Versionen
John Owen Chile
Ich benutze Squeel seit einigen Jahren und bin immer noch zufrieden damit. Aber vielleicht lohnt es sich, ein anderes ORM auszuprobieren, wie zum Beispiel die Fortsetzung (<> squeel), die vielversprechende nette Funktionen zu bieten scheint, um ActiveRecord zu ersetzen.
Douglas
5

Arel ist dein Freund.

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

Joegiralt
quelle
4

Eine andere ausgefallene Möglichkeit ist ...

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

Mit dieser Funktion können Sie verständlichere Abfragen erstellen, wenn Sie diese an mehreren Stellen ersetzen möchten, z.

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

Dies hat mehr Bedeutung als viel ?auf der Abfrage zu haben ...

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

Ich habe oft dieses Problem mit Datumsfeldern (wo Vergleichsoperatoren sehr häufig sind).

Die Antwort von Mihai näher zu erläutern, die ich für einen soliden Ansatz halte.

Zu den Modellen können Sie folgende Bereiche hinzufügen:

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

... und dann in Ihrem Controller oder wo immer Sie Ihr Modell verwenden:

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

... ein komplexeres Beispiel mit Joins sieht folgendermaßen aus:

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

Ein großer Vorteil dieses Ansatzes besteht darin, dass (a) Sie Ihre Abfragen aus verschiedenen Bereichen zusammenstellen können und (b) Alias-Kollisionen vermieden werden, wenn Sie zweimal derselben Tabelle beitreten, da arel_table diesen Teil der Abfragegenerierung verarbeitet.

Brett Green
quelle
0

Schienen 6.1+

Rails 6.1 hat eine neue 'Syntax' für Vergleichsoperatoren unter whereBedingungen hinzugefügt , zum Beispiel:

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

So kann Ihre Anfrage wie folgt umgeschrieben werden:

User.where('id >': 200) 

Hier ist ein Link zu PR, wo Sie weitere Beispiele finden.

Marian13
quelle
-3

Kürzere:

User.where("id > 200")
Bengala
quelle
8
Ich gehe davon aus, dass dies dynamisch sein muss und dass das Poster die SQL-Injection durch die Verwendung parametrisierter Abfragen ( where("id > ?", 200)Syntax) vermeiden möchte . Das erreicht das nicht.
Matthew Hinea