Rotaie in cui condizione utilizzando NOT NIL


359

Utilizzando lo stile rails 3 come scriverei l'opposto di:

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

Voglio trovare dove ID NON è zero. Provai:

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

Ma questo ritorna:

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

Non è sicuramente quello di cui ho bisogno e sembra quasi un bug in ARel.


2
!nilrestituisce truein Ruby, e AREL traduce truead 1in una query SQL. Quindi la query generata è in effetti ciò che hai richiesto: non si trattava di un bug ARel.
Yuval,

Risposte:


510

Il modo canonico per farlo con Rails 3:

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

ActiveRecord 4.0 e versioni successive where.notconsentono di eseguire questa operazione:

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

Quando lavoro con gli ambiti tra le tabelle, preferisco sfruttare in mergemodo da poter utilizzare gli ambiti esistenti più facilmente.

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

Inoltre, poiché includesnon sempre si sceglie una strategia di join, è necessario utilizzare anche referencesqui, altrimenti si potrebbe finire con SQL non valido.

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

1
L'ultimo qui non funziona per me, abbiamo bisogno di una gemma o plugin extra per questo? Ricevo: rails undefined method 'not_eq' for :confirmed_at:Symbol..
Tim Baas il

3
@Tim Sì, la gemma MetaWhere che ho collegato sopra.
Adam Lassek,

3
Mi piace la soluzione che non richiede altre gemme :) anche se è un po 'brutta
oreoshake,

1
Vale la pena avere @oreoshake MetaWhere / Squeel, questa è solo una piccola sfaccettatura. Ma ovviamente è bene sapere un caso generale.
Adam Lassek,

1
@BKSpurgeon Il concatenamento delle wherecondizioni sta semplicemente costruendo un AST, non colpisce il database fino a quando non si raggiunge un metodo terminale come eacho to_a. La creazione della query non è un problema di prestazioni; quello che stai richiedendo dal database è.
Adam Lassek,

251

Non è un bug in ARel, è un bug nella tua logica.

Quello che vuoi qui è:

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

2
Sono curioso di
sapere

12
A indovinare, ritorna! Nil true, che è un valore booleano. :id => trueti porta id = 1in SQLese.
zetetico il

Questo è un buon modo per evitare di scrivere frammenti sql grezzi. La sintassi non è così concisa come Squeel però.
Kelvin,

1
Non sono riuscito a farlo con sqlite3. sqlite3 vuole vedere field_name != 'NULL'.
mjnissim,

@zetetic A meno che tu non stia usando Postgres, nel qual caso ottieni id = 't':)
thekingoftruth

36

Per Rails4:

Quindi, quello che vuoi è un join interno, quindi dovresti semplicemente usare il predicato join:

  Foo.joins(:bar)

  Select * from Foo Inner Join Bars ...

Ma, per la cronaca, se vuoi una condizione "NOT NULL" usa semplicemente il non predicato:

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

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

Nota che questa sintassi riporta una deprecazione (parla di uno snippet di stringa SQL, ma immagino che la condizione di hash sia cambiata in stringa nel parser?), Quindi assicurati di aggiungere i riferimenti alla fine:

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

AVVISO DI DEPRECAZIONE: Sembra che tu sia desideroso di caricare le tabelle (una delle: ....) a cui si fa riferimento in uno snippet SQL di stringa. Per esempio:

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

Attualmente, Active Record riconosce la tabella nella stringa e sa UNIRE la tabella dei commenti alla query, anziché caricare i commenti in una query separata. Tuttavia, farlo senza scrivere un parser SQL completo è intrinsecamente imperfetto. Poiché non vogliamo scrivere un parser SQL, stiamo rimuovendo questa funzionalità. Da ora in poi, devi dire esplicitamente ad Active Record quando fai riferimento a una tabella da una stringa:

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

1
La referenceschiamata mi ha aiutato!
theblang


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.