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のバグのように思えます。
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))
これはARelのバグではなく、ロジックのバグです。
ここで欲しいのは:
Foo.includes(:bar).where(Bar.arel_table[:id].not_eq(nil))
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スニペットについて話していますが、ハッシュ条件はパーサーではstringに変更されていると思いますか?)。
Foo.includes(:bar).where.not(bars: {id: nil}).references(:bar)
非推奨警告:文字列のSQLスニペットで参照されているテーブル(:....の1つ)をロードしようとしているようです。例えば:
Post.includes(:comments).where("comments.title = 'foo'")
現在、Active Recordは文字列内のテーブルを認識し、別のクエリでコメントを読み込むのではなく、コメントテーブルをクエリに結合することを認識しています。しかし、本格的なSQLパーサーを書かずにこれを行うことは本質的に欠陥があります。 SQLパーサーを書きたくないので、この機能を削除しています。これからは、文字列からテーブルを参照するときにActive Recordに明示的に伝えなければなりません。
Post.includes(:comments).where("comments.title = 'foo'").references(:comments)
これは確かではありませんが、Rails 4ではこれが私にとって役に立ちました。
Foo.where.not(bar: nil)
Rails 4なら簡単です。
Foo.includes(:bar).where.not(bars: {id: nil})
http://guides.rubyonrails.org/active_record_querying.html#not-conditions もご覧ください。