Szyny, w których warunek jest użyty NIE NIL


359

Używając stylu szyn 3, jak napisałbym przeciwieństwo:

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

Chcę dowiedzieć się, gdzie identyfikator NIE jest zerowy. Próbowałem:

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

Ale to zwraca:

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

To zdecydowanie nie jest to, czego potrzebuję i prawie wydaje się błędem w ARel.


2
!nilZwraca truew Ruby i AREL przekłada truesię 1w zapytaniu SQL. Tak więc wygenerowane zapytanie jest tym, o co prosiłeś - nie był to błąd ARel.
yuval

Odpowiedzi:


510

Kanoniczny sposób, aby to zrobić za pomocą Rails 3:

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

ActiveRecord 4.0 i wyżej dodaje where.not, abyś mógł to zrobić:

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

Podczas pracy z zakresami między tabelami wolę korzystać z dźwigni, mergeaby łatwiej korzystać z istniejących zakresów.

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

Ponadto, ponieważ includesnie zawsze wybiera strategię łączenia, należy również użyć referencestutaj, w przeciwnym razie może dojść do nieprawidłowego SQL.

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

1
Ostatni tutaj nie działa dla mnie, czy potrzebujemy do tego dodatkowego klejnotu lub wtyczki? Dostaję: rails undefined method 'not_eq' for :confirmed_at:Symbol..
Tim Baas,

3
@Tim Tak, klejnot MetaWhere, który podłączyłem powyżej.
Adam Lassek

3
Podoba mi się rozwiązanie, które nie wymaga innych klejnotów :) nawet jeśli jest trochę brzydkie
oreoshake

1
@oreoshake MetaWhere / Squeel są warte posiadania, to tylko mały aspekt. Ale oczywiście ogólny przypadek jest dobrze znany.
Adam Lassek

1
@BKSpurgeon whereWarunki łączenia są po prostu budowaniem AST, nie trafiają do bazy danych, dopóki nie trafisz na terminalową metodę, taką jak eachlub to_a. Tworzenie zapytania nie ma wpływu na wydajność; o to prosisz z bazy danych.
Adam Lassek,

251

To nie jest błąd w ARel, to błąd w twojej logice.

Tutaj chcesz:

Foo.includes(:bar).where(Bar.arel_table[:id].not_eq(nil))

2
Jestem ciekawy, jaka jest logika zamiany!
Zero

12
Domyślnie, zwraca! Nil true, co jest wartością logiczną. :id => truedostanie cię id = 1w SQLese.
zetetic

Jest to dobry sposób na uniknięcie pisania surowych fragmentów sql. Składnia nie jest jednak tak zwięzła jak Squeel.
Kelvin

1
Nie udało mi się tego zrobić z sqlite3. sqlite3 chce zobaczyć field_name != 'NULL'.
mjnissim

@zetetic O ile nie używasz postgresu, w takim przypadku dostajesz id = 't':)
thekingoftruth

36

Dla Rails4:

Więc to, czego chcesz, to połączenie wewnętrzne, więc naprawdę powinieneś po prostu użyć predykatu złączeń:

  Foo.joins(:bar)

  Select * from Foo Inner Join Bars ...

Ale, dla przypomnienia, jeśli chcesz warunek „NOT NULL”, po prostu użyj predykatu:

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

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

Zauważ, że ta składnia zgłasza wycofanie (mówi o fragmencie kodu SQL, ale myślę, że warunek skrótu został zmieniony na string w parserze?), Więc pamiętaj, aby dodać odwołania na końcu:

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

OSTRZEŻENIE DOTYCZĄCE DEPRECACJI: Wygląda na to, że chętnie ładujesz tabele (jeden z: ....), do których odwołuje się ciąg kodu SQL. Na przykład:

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

Obecnie Active Record rozpoznaje tabelę w ciągu i wie, DOŁĄCZYĆ tabelę komentarzy do zapytania, zamiast ładować komentarze w oddzielnym zapytaniu. Jednak robienie tego bez pisania pełnego parsera SQL jest z natury wadliwe. Ponieważ nie chcemy pisać analizatora składni SQL, usuwamy tę funkcję. Odtąd musisz jawnie informować Active Record, gdy odwołujesz się do tabeli z ciągu:

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

1
referencesRozmowa pomogła mi!
theblang


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.