Schienen wo Zustand mit NICHT NIL

359

Wie würde ich mit dem Rails 3-Stil das Gegenteil von schreiben:

Foo.includes(:bar).where(:bars=>{:id=>nil})

Ich möchte herausfinden, wo id NICHT Null ist. Ich habe es versucht:

Foo.includes(:bar).where(:bars=>{:id=>!nil}).to_sql

Aber das kehrt zurück:

=> "SELECT     \"foos\".* FROM       \"foos\"  WHERE  (\"bars\".\"id\" = 1)"

Das ist definitiv nicht das, was ich brauche, und scheint fast wie ein Fehler in ARel.

SooDesuNe
quelle
2
!nilauswertet , um truein Ruby und Arel übersetzt , trueum 1in einer SQL - Abfrage. Die generierte Abfrage ist also genau das, wonach Sie gefragt haben - dies war kein ARel-Fehler.
Yuval

Antworten:

510

Der kanonische Weg, dies mit Rails 3 zu tun:

Foo.includes(:bar).where("bars.id IS NOT NULL")

ActiveRecord 4.0 und höher fügt hinzu, where.notdamit Sie dies tun können:

Foo.includes(:bar).where.not('bars.id' => nil)
Foo.includes(:bar).where.not(bars: { id: nil })

Wenn ich mit Bereichen zwischen Tabellen arbeite, bevorzuge ich die Hebelwirkung, mergedamit ich vorhandene Bereiche einfacher verwenden kann.

Foo.includes(:bar).merge(Bar.where.not(id: nil))

Da includesnicht immer eine Join-Strategie ausgewählt wird, sollten Sie diese auch referenceshier verwenden, da Sie sonst möglicherweise ungültiges SQL erhalten.

Foo.includes(:bar)
   .references(:bar)
   .merge(Bar.where.not(id: nil))
Adam Lassek
quelle
1
Das letzte hier funktioniert nicht für mich. Brauchen wir dafür ein zusätzliches Juwel oder Plugin? Ich bekomme: rails undefined method 'not_eq' for :confirmed_at:Symbol..
Tim Baas
3
@ Tim Ja, das MetaWhere-Juwel, das ich oben verlinkt habe.
Adam Lassek
3
Ich mag die Lösung, die keine anderen Edelsteine ​​erfordert :) auch wenn es ein bisschen hässlich ist
Oreoshake
1
@oreoshake MetaWhere / Squeel sind einen Besuch wert, dies ist nur eine winzige Facette. Aber natürlich ist ein allgemeiner Fall gut zu wissen.
Adam Lassek
1
@BKSpurgeon Verkettungsbedingungen erstellen whereeinfach einen AST. Er trifft die Datenbank erst, wenn Sie eine Terminalmethode wie eachoder treffen to_a. Das Erstellen der Abfrage ist kein Leistungsproblem. Was Sie von der Datenbank anfordern, ist.
Adam Lassek
251

Es ist kein Fehler in ARel, es ist ein Fehler in Ihrer Logik.

Was Sie hier wollen, ist:

Foo.includes(:bar).where(Bar.arel_table[:id].not_eq(nil))
Ryan Bigg
quelle
2
Ich bin gespannt, was die Logik ist, um! Null in '1' zu
verwandeln
12
Vermutlich kehrt! Nil zurück true, was ein Boolescher Wert ist. :id => truebringt Sie id = 1in SQLese.
Zetetic
Dies ist ein guter Weg, um das Schreiben von rohen SQL-Fragmenten zu vermeiden. Die Syntax ist jedoch nicht so präzise wie bei Squeel.
Kelvin
1
Ich habe es mit sqlite3 nicht geschafft. will sqlite3 sehen field_name != 'NULL'.
mjnissim
@ Zetetic Es sei denn, Sie verwenden Postgres, in diesem Fall erhalten Sie id = 't':)
thekingoftruth
36

Für Rails4:

Was Sie also wollen, ist eine innere Verknüpfung. Sie sollten also wirklich nur das Prädikat "Verknüpfungen" verwenden:

  Foo.joins(:bar)

  Select * from Foo Inner Join Bars ...

Wenn Sie jedoch eine "NOT NULL" -Bedingung wünschen, verwenden Sie einfach das Prädikat not:

Foo.includes(:bar).where.not(bars: {id: nil})

Select * from Foo Left Outer Join Bars on .. WHERE bars.id IS NOT NULL

Beachten Sie, dass diese Syntax eine Abwertung meldet (es handelt sich um ein String-SQL-Snippet, aber ich denke, die Hash-Bedingung wird im Parser in String geändert?). Fügen Sie daher die Verweise am Ende hinzu:

Foo.includes(:bar).where.not(bars: {id: nil}).references(:bar)

DEPRECATION WARNING: Es sieht so aus, als würden Sie gerne Tabellen (eine von: ....) laden, auf die in einem String-SQL-Snippet verwiesen wird. Zum Beispiel:

Post.includes(:comments).where("comments.title = 'foo'")

Derzeit erkennt Active Record die Tabelle in der Zeichenfolge und kann der Kommentartabelle der Abfrage beitreten, anstatt Kommentare in eine separate Abfrage zu laden. Dies zu tun, ohne einen vollständigen SQL-Parser zu schreiben, ist jedoch von Natur aus fehlerhaft. Da wir keinen SQL-Parser schreiben möchten, entfernen wir diese Funktionalität. Von nun an müssen Sie Active Record explizit mitteilen, wenn Sie auf eine Tabelle aus einer Zeichenfolge verweisen:

Post.includes(:comments).where("comments.title = 'foo'").references(:comments)
Matt Rogish
quelle
1
Der referencesAnruf hat mir geholfen!
Theblang
27

Ich bin mir nicht sicher, ob dies hilfreich ist, aber das hat bei mir in Rails 4 funktioniert

Foo.where.not(bar: nil)
Raed Tulefat
quelle
1
Dies ist die beste Antwort hier. - 2017 robots.thoughtbot.com/activerecords-wherenot
dezman