ActiveRecord ODER Abfrage


180

Wie führen Sie eine ODER-Abfrage in Rails 3 ActiveRecord durch? Alle Beispiele, die ich finde, haben nur UND-Abfragen.

Bearbeiten: Die OR- Methode ist seit Rails 5 verfügbar. Siehe ActiveRecord :: QueryMethods


7
Lernen Sie SQL. ActiveRecord macht es einfach, Dinge zum Laufen zu bringen, aber Sie müssen immer noch wissen, was Ihre Abfragen tun, sonst kann Ihr Projekt möglicherweise nicht skaliert werden. Ich dachte auch, ich könnte durchkommen, ohne mich jemals mit SQL befassen zu müssen, und ich habe mich sehr geirrt.
Ryan0

1
@ Ryan0, du hast so recht. Wir können ausgefallene Edelsteine ​​und andere Dinge verwenden, aber wir sollten uns darüber im Klaren sein, was diese Edelsteine ​​im Inneren tun und was die zugrunde liegende Technologie ist, und möglicherweise die zugrunde liegenden Technologien ohne die Hilfe des Edelsteins verwenden, falls dies erforderlich sein sollte Performance. Edelsteine ​​werden unter Berücksichtigung einer bestimmten Anzahl von Anwendungsfällen erstellt. Es kann jedoch Situationen geben, in denen einer der Anwendungsfälle in unserem Projekt unterschiedlich sein kann.
Rubyprince

2
Kann

1
Seit Rails 5 sollte IMHO die akzeptierte Antwort Greg Olsen Version sein:Post.where(column: 'something').or(Post.where(other: 'else'))
Philipp Claßen

6
Diese Frage hat ein Tag von Ruby-on-Rails-3. Warum sollte sich die akzeptierte Antwort nur auf Rails 5 beziehen?
iNulty

Antworten:


109

Verwenden Sie ARel

t = Post.arel_table

results = Post.where(
  t[:author].eq("Someone").
  or(t[:title].matches("%something%"))
)

Das resultierende SQL:

ree-1.8.7-2010.02 > puts Post.where(t[:author].eq("Someone").or(t[:title].matches("%something%"))).to_sql
SELECT     "posts".* FROM       "posts"  WHERE     (("posts"."author" = 'Someone' OR "posts"."title" LIKE '%something%'))

Fühlt sich ein bisschen chaotisch an, aber zumindest schreibe ich kein SQL, was sich einfach falsch anfühlt! Ich werde mich mehr mit Arel befassen müssen.
Pho3nixf1re

54
Ich kann nicht glauben, wie viel eleganter Sequel ist
Macario

3
An SQL ist nichts auszusetzen. Was schief gehen kann, ist, wie wir die SQL-Zeichenfolge SQL Injection erstellen . Daher müssen wir die Benutzereingaben bereinigen, bevor wir sie einer SQL-Abfrage zur Verfügung stellen, die für die Datenbank ausgeführt werden muss. Dies wird von ORMs erledigt , und diese haben die Randfälle behandelt, die wir tendenziell übersehen. Daher ist es immer ratsam, ORM zum Erstellen von SQL-Abfragen zu verwenden.
Rubyprince

5
Ein weiteres Problem mit SQL ist, dass es nicht datenbankunabhängig ist. Zum Beispiel matcheswird LIKEfür SQLite (standardmäßig ohne ILIKEBerücksichtigung der Groß- und Kleinschreibung) und auf Postgres (benötigt explizite Groß- und Kleinschreibung wie Operator) erzeugt.
Amöbe

1
@ToniLeigh ActiveRecord verwendet SQL unter der Haube. Es ist nur eine Syntax zum Schreiben von SQL-Abfragen, die die SQL-Bereinigung behandelt und Ihre Abfragen DB-unabhängig macht. Der einzige Aufwand besteht darin, dass Rails die Syntax in eine SQL-Abfrage konvertiert. Und es ist nur eine Frage von Millisekunden. Natürlich sollten Sie die leistungsstärkste SQL-Abfrage schreiben und versuchen, sie in die ActiveRecord-Syntax zu konvertieren. Wenn SQL nicht in der ActiveRecord-Syntax dargestellt werden kann, sollten Sie sich für einfaches SQL entscheiden.
Rubyprince

208

Wenn Sie einen ODER-Operator für den Wert einer Spalte verwenden möchten, können Sie ein Array an übergeben, .whereund ActiveRecord verwendet IN(value,other_value):

Model.where(:column => ["value", "other_value"]

Ausgänge:

SELECT `table_name`.* FROM `table_name` WHERE `table_name`.`column` IN ('value', 'other_value')

Dies sollte das Äquivalent von a ORauf einer einzelnen Spalte erreichen


13
Dies ist die sauberste "Rails Way" Antwort, hätte akzeptiert werden sollen
Koonse

10
Dank @koonse handelt es sich nicht gerade um eine ODER-Abfrage, aber für diese Situation wird das gleiche Ergebnis erzielt .
Deadkarma


79
Diese Lösung ist kein Ersatz für OR, da Sie auf diese Weise nicht zwei verschiedene Spalten verwenden können.
Fotanus

151

in Rails 3 sollte es sein

Model.where("column = ? or other_column = ?", value, other_value)

Dies schließt auch Raw SQL ein, aber ich glaube nicht, dass es in ActiveRecord eine Möglichkeit gibt, eine ODER-Operation durchzuführen. Ihre Frage ist keine Noob-Frage.


40
Selbst wenn es rohes SQL gibt - diese Aussage sieht für mich viel klarer aus als Arel. Vielleicht sogar DRYer.
Jibiel

6
Wenn Sie andere Tabellenverknüpfungen verketten müssen, die möglicherweise Spalten mit denselben Spaltennamen haben, sollten Sie sie wie Page.where("pages.column = ? or pages.other_column = ?", value, other_value)
folgt verwenden

1
Und das schlägt fehl, wenn die Abfrage die Tabellen aliasisiert. Dann scheint Arel.
DGM

58

Eine aktualisierte Version von Rails / ActiveRecord unterstützt diese Syntax möglicherweise nativ. Es würde ähnlich aussehen wie:

Foo.where(foo: 'bar').or.where(bar: 'bar')

Wie in dieser Pull-Anfrage angegeben, https://github.com/rails/rails/pull/9052

Im Moment funktioniert es großartig, einfach bei Folgendem zu bleiben:

Foo.where('foo= ? OR bar= ?', 'bar', 'bar')

Update: Laut https://github.com/rails/rails/pull/16052 wird die orFunktion in Rails 5 verfügbar sein

Update: Die Funktion wurde mit dem Rails 5-Zweig zusammengeführt


2
orist jetzt verfügbar Rails 5, kann jedoch nicht auf diese Weise implementiert werden, da erwartet wird, dass 1 Argument übergeben wird. Es erwartet ein Arel-Objekt. Siehe die akzeptierte Antwort
El'Magnifico

2
Die orMethode in ActiveRecord funktioniert, wenn Sie sie direkt verwenden. Sie kann jedoch Ihre Erwartungen brechen, wenn sie in einem Bereich verwendet wird, der dann verkettet wird.
Jeremy List

Was @JeremyList gesagt hat, ist genau richtig. Das hat mich hart gebissen.
Courtsimas


23

Schienen 5 werden mit einer orMethode geliefert. (l Tinte zur Dokumentation )

Diese Methode akzeptiert ein ActiveRecord::RelationObjekt. z.B:

User.where(first_name: 'James').or(User.where(last_name: 'Scott'))

14

Wenn Sie Arrays als Argumente verwenden möchten, funktioniert der folgende Code in Rails 4:

query = Order.where(uuid: uuids, id: ids)
Order.where(query.where_values.map(&:to_sql).join(" OR "))
#=> Order Load (0.7ms)  SELECT "orders".* FROM "orders" WHERE ("orders"."uuid" IN ('5459eed8350e1b472bfee48375034103', '21313213jkads', '43ujrefdk2384us') OR "orders"."id" IN (2, 3, 4))

Weitere Informationen: ODER-Abfragen mit Arrays als Argumente in Rails 4 .


9
Oder dies: Order.where(query.where_values.inject(:or))Arel den ganzen Weg benutzen.
Cameron Martin

> ODER Abfragen mit Arrays als Argumente in Rails 4. Nützlicher Link! Vielen Dank!
Zeke Fast

7

Das MetaWhere- Plugin ist absolut erstaunlich.

Mischen Sie einfach ORs und ANDs, schließen Sie sich den Bedingungen für jede Zuordnung an und geben Sie sogar OUTER JOINs an!

Post.where({sharing_level: Post::Sharing[:everyone]} | ({sharing_level: Post::Sharing[:friends]} & {user: {followers: current_user} }).joins(:user.outer => :followers.outer}

6

Fügen Sie einfach ein ODER in die Bedingungen ein

Model.find(:all, :conditions => ["column = ? OR other_column = ?",value, other_value])

4
Dies ist eher die Syntax für Rails 2, und ich muss mindestens einen Teil eines SQL-Strings schreiben.
Pho3nixf1re

3

Ich habe gerade dieses Plugin aus der Client-Arbeit extrahiert , mit der Sie Bereiche mit z .or.. Post.published.or.authored_by(current_user). Squeel (neuere Implementierung von MetaSearch) ist ebenfalls großartig, lässt Sie jedoch keine ODER-Bereiche zu, sodass die Abfragelogik etwas redundant werden kann.


3

Mit Rails + Arel ein klarerer Weg:

# Table name: messages
#
# sender_id:    integer
# recipient_id: integer
# content:      text

class Message < ActiveRecord::Base
  scope :by_participant, ->(user_id) do
    left  = arel_table[:sender_id].eq(user_id)
    right = arel_table[:recipient_id].eq(user_id)

    where(Arel::Nodes::Or.new(left, right))
  end
end

Produziert:

$ Message.by_participant(User.first.id).to_sql 
=> SELECT `messages`.* 
     FROM `messages` 
    WHERE `messages`.`sender_id` = 1 
       OR `messages`.`recipient_id` = 1

3

Sie könnten es tun wie:

Person.where("name = ? OR age = ?", 'Pearl', 24)

oder eleganter, installiere rails_or gem und mache es wie folgt :

Person.where(:name => 'Pearl').or(:age => 24)

-2

Ich möchte hinzufügen, dass dies eine Lösung ist, um mehrere Attribute eines ActiveRecord zu durchsuchen. Schon seit

.where(A: param[:A], B: param[:B])

sucht nach A und B.


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.