NOT NILを使用した状態のレール


359

rails 3スタイルを使用して、私はどのように反対のことを書けますか?

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

idがnilでない場所を見つけたいのですが。私は試した:

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

しかし、それは戻ります:

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

これは間違いなく私が必要としているものではなく、ARelのバグのように見えます。


2
!niltrueRubyで評価され、ARel はSQLクエリで変換true1れます。したがって、生成されたクエリは実際に要求したものです-これはARelのバグではありませんでした。
yuval 2017

回答:


510

Rails 3でこれを行うための標準的な方法:

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

ActiveRecord 4.0以上が追加されているwhere.notので、これを行うことができます:

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

テーブル間のスコープを操作するときmergeは、既存のスコープをより簡単に使用できるように活用することを好みます。

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

また、includesは常に結合戦略を選択するわけではないため、referencesここでも使用する必要があります。そうしないと、SQLが無効になる可能性があります。

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

1
最後の1つは私にとっては機能しません。これには追加のGemまたはプラグインが必要ですか?私は得る:rails undefined method 'not_eq' for :confirmed_at:Symbol..
Tim Baas

3
@Timはい、上記でリンクしたMetaWhere宝石です。
Adam Lassek

3
他の宝石を必要としないソリューション:)それは少し醜いの場合でも同様I
oreoshakeは

1
@ore​​oshake MetaWhere / Squeelを使う価値は十分あります。これはほんの小さな面です。しかし、もちろん一般的なケースは知っておくとよいでしょう。
アダム・ラセク

1
@BKSpurgeonチェーニングwhere条件はASTを構築するだけであり、eachまたはのようなターミナルメソッドを押すまでデータベースにヒットしませんto_a。クエリの作成はパフォーマンスの問題ではありません。あなたがデータベースから要求しているものは何ですか。
Adam Lassek

251

これはARelのバグではなく、ロジックのバグです。

ここに欲しいのは:

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

2
!nilを '1'に変換するためのロジックは何なのか、私は
知りたい

12
一見すると、!nilはtrueブール値であるを返します。SQLeseで:id => trueあなたid = 1を取得します。
10

これは、未加工のSQLフラグメントの書き込みを回避するための良い方法です。ただし、構文はSqueelほど簡潔ではありません。
ケルビン

1
私はこれをsqlite3でなんとかしていません。sqlite3は見たいfield_name != 'NULL'です。
mjnissim 2012

@zetetic postgresを使用している場合をid = 't'
除いて、

36

Rails4の場合:

したがって、必要なのは内部結合なので、実際には結合述語を使用する必要があります。

  Foo.joins(:bar)

  Select * from Foo Inner Join Bars ...

ただし、レコードについて、「NOT NULL」条件が必要な場合は、単にnot述語を使用します。

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

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

この構文は非推奨を報告することに注意してください(文字列SQLスニペットについて話しますが、パーサーでハッシュ条件が文字列に変更されたと思いますか?)、最後に参照を追加してください:

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

サポート終了の警告:文字列SQLスニペットで参照されているテーブル(次のいずれか...)を積極的にロードしているようです。例えば:

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

現在、Active Recordは文字列内のテーブルを認識し、コメントを別のクエリに読み込むのではなく、コメントテーブルをクエリに結合することを認識しています。ただし、本格的なSQLパーサーを作成せずにこれを行うと、本質的に欠陥があります。SQLパーサーを作成したくないので、この機能を削除します。今後は、文字列からテーブルを参照するときに、Active Recordに明示的に通知する必要があります。

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

1
referencesコールが助けてくれました!
theblang 2017年


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.