Rails dimana kondisinya menggunakan NOT NIL


359

Menggunakan rails 3 style bagaimana saya menulis kebalikan dari:

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

Saya ingin menemukan di mana id TIDAK nol. Saya mencoba:

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

Tapi itu kembali:

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

Itu jelas bukan yang saya butuhkan, dan hampir seperti bug di ARel.


2
!nildievaluasi truedalam Ruby, dan ARel diterjemahkan trueke 1dalam query SQL. Jadi permintaan yang dihasilkan sebenarnya adalah apa yang Anda minta - ini bukan bug ARel.
yuval

Jawaban:


510

Cara kanonik untuk melakukan ini dengan Rails 3:

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

ActiveRecord 4.0 dan di atasnya menambahkan where.notsehingga Anda dapat melakukan ini:

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

Saat bekerja dengan cakupan antara tabel, saya lebih suka memanfaatkan mergesehingga saya dapat menggunakan cakupan yang ada dengan lebih mudah.

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

Juga, karena includestidak selalu memilih strategi bergabung, Anda harus menggunakan di referencessini juga, jika tidak, Anda mungkin berakhir dengan SQL yang tidak valid.

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

1
Yang terakhir di sini tidak berfungsi untuk saya, apakah kita memerlukan permata atau plugin tambahan untuk ini? Saya mendapatkan: rails undefined method 'not_eq' for :confirmed_at:Symbol..
Tim Baas

3
@Tim Ya, permata MetaWhere yang saya tautkan di atas.
Adam Lassek

3
Saya suka solusi yang tidak memerlukan permata lain :) bahkan jika itu agak jelek
oreoshake

1
@oreoshake MetaWhere / Squeel layak dimiliki, ini hanya aspek kecil. Tapi tentu saja kasus umum itu baik untuk diketahui.
Adam Lassek

1
@BKSpurgeon Chaining wherekondisi hanya membangun AST, itu tidak mengenai database sampai Anda menekan metode terminal seperti eachatau to_a. Membangun kueri bukan masalah kinerja; apa yang Anda minta dari database.
Adam Lassek

251

Ini bukan bug di ARel, ini bug dalam logika Anda.

Yang Anda inginkan di sini adalah:

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

2
Saya ingin tahu apa logikanya untuk mengubah! Nil menjadi '1'
SooDesuNe

12
Di tebak,! Nil kembali true, yang merupakan boolean. :id => truemembuat Anda id = 1dalam SQLese.
zetetic

Ini adalah cara yang baik untuk menghindari penulisan fragmen sql mentah. Sintaksnya tidak sesingkat Squeel.
Kelvin

1
Saya belum berhasil melakukan ini dengan sqlite3. sqlite3 ingin melihat field_name != 'NULL'.
mjnissim

@etiketika Kecuali Anda menggunakan postgres, dalam hal ini Anda dapatkan id = 't':)
thekingoftruth

36

Untuk Rails4:

Jadi, yang Anda inginkan adalah gabungan dari dalam, jadi Anda harus menggunakan predikat gabungan:

  Foo.joins(:bar)

  Select * from Foo Inner Join Bars ...

Tetapi, sebagai catatan, jika Anda menginginkan kondisi "NOT NULL" cukup gunakan predikat tidak:

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

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

Perhatikan bahwa sintaks ini melaporkan penghentian (ini berbicara tentang snipet string SQL, tapi saya kira kondisi hash diubah menjadi string di parser?), Jadi pastikan untuk menambahkan referensi ke akhir:

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

PERINGATAN DEPRECASI: Sepertinya Anda ingin memuat tabel (s) (salah satu dari: ....) yang direferensikan dalam snippet string SQL. Sebagai contoh:

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

Saat ini, Rekaman Aktif mengenali tabel dalam string, dan tahu untuk BERGABUNG dengan tabel komentar ke kueri, daripada memuat komentar dalam kueri yang terpisah. Namun, melakukan ini tanpa menulis parser SQL full-blown secara inheren cacat. Karena kami tidak ingin menulis parser SQL, kami menghapus fungsi ini. Mulai sekarang, Anda harus memberi tahu Rekaman Aktif secara eksplisit saat Anda mereferensikan tabel dari string:

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

1
The referencespanggilan membantu saya!
theblang


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.