Rails nơi điều kiện sử dụng KHÔNG NIL


359

Sử dụng kiểu 3 đường ray làm thế nào tôi có thể viết ngược lại:

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

Tôi muốn tìm nơi id KHÔNG. Tôi đã thử:

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

Nhưng điều đó trả về:

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

Đó chắc chắn không phải là thứ tôi cần, và gần như là một lỗi trong ARel.


2
!nilước tính truetrong Ruby và ARel chuyển truethành 1truy vấn SQL. Vì vậy, truy vấn được tạo trong thực tế là những gì bạn yêu cầu - đây không phải là lỗi ARel.
yuval

Câu trả lời:


510

Cách chính tắc để làm điều này với Rails 3:

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

ActiveRecord 4.0 trở lên bổ sung where.notđể bạn có thể làm điều này:

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

Khi làm việc với phạm vi giữa các bảng, tôi thích tận dụng mergeđể tôi có thể sử dụng phạm vi hiện có dễ dàng hơn.

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

Ngoài ra, vì includeskhông phải lúc nào cũng chọn chiến lược tham gia, bạn cũng nên sử dụng referencesở đây, nếu không bạn có thể kết thúc bằng SQL không hợp lệ.

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

1
Cuối cùng ở đây không làm việc cho tôi, chúng ta có cần thêm đá quý hoặc plugin cho việc này không? Tôi nhận được: rails undefined method 'not_eq' for :confirmed_at:Symbol..
Tim Baas

3
@Tim Vâng, đá quý MetaWhere tôi đã liên kết ở trên.
Adam Lassek

3
Tôi thích giải pháp không cần đá quý khác :) ngay cả khi nó hơi xấu
oreoshake

1
@oreoshake MetaWhere / Squeel rất đáng để có, đây chỉ là một khía cạnh nhỏ. Nhưng tất nhiên một trường hợp chung là tốt để biết.
Adam Lassek

1
@BKSpurgeon whereĐiều kiện xâu chuỗi chỉ đơn giản là xây dựng AST, nó không đánh vào cơ sở dữ liệu cho đến khi bạn nhấn một phương thức đầu cuối như eachhoặc to_a. Xây dựng truy vấn không phải là một mối quan tâm hiệu suất; những gì bạn yêu cầu từ cơ sở dữ liệu là.
Adam Lassek

251

Đó không phải là lỗi trong ARel, đó là lỗi trong logic của bạn.

Những gì bạn muốn ở đây là:

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

2
Tôi tò mò không biết logic là gì khi biến! Nil thành '1'
SooDesuNe

12
Tại một phỏng đoán ,! Nil trở lại true, đó là một boolean. :id => truegiúp bạn id = 1trong SQLese.
zetetic

Đây là một cách tốt để tránh viết các đoạn sql thô. Cú pháp không ngắn gọn như Squeel.
Kelvin

1
Tôi đã không quản lý để làm điều này với sqlite3. sqlite3 muốn xem field_name != 'NULL'.
mjnissim

@zetetic Trừ khi bạn đang sử dụng postgres, trong trường hợp bạn nhận được id = 't':)
thekingoftruth

36

Đối với Rails4:

Vì vậy, những gì bạn muốn là một tham gia bên trong, vì vậy bạn thực sự chỉ nên sử dụng vị từ tham gia:

  Foo.joins(:bar)

  Select * from Foo Inner Join Bars ...

Nhưng, đối với bản ghi, nếu bạn muốn một điều kiện "KHÔNG NULL" chỉ cần sử dụng vị từ không:

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

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

Lưu ý rằng cú pháp này báo cáo sự phản đối (nó nói về đoạn mã SQL, nhưng tôi đoán điều kiện băm được thay đổi thành chuỗi trong trình phân tích cú pháp?), Vì vậy hãy chắc chắn để thêm các tham chiếu vào cuối:

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

CẢNH BÁO KHAI THÁC: Có vẻ như bạn đang háo hức tải (các) bảng (một trong số: ....) được tham chiếu trong đoạn mã SQL. Ví dụ:

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

Hiện tại, Active Record nhận ra bảng trong chuỗi và biết THAM GIA bảng nhận xét vào truy vấn, thay vì tải nhận xét trong một truy vấn riêng. Tuy nhiên, làm điều này mà không cần viết một trình phân tích cú pháp SQL đầy đủ vốn là thiếu sót. Vì chúng tôi không muốn viết một trình phân tích cú pháp SQL, chúng tôi sẽ xóa chức năng này. Từ giờ trở đi, bạn phải thông báo rõ ràng cho Active Record khi bạn đang tham chiếu một bảng từ một chuỗi:

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

1
Cuộc referencesgọi đã giúp tôi!
theblang


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.