Rails 4.1でAND,OR,副問い合わせを組み合わせたSQLを書く方法
例えば以下のようなSQLを書きたいとき
SELECT date,name
FROM book
WHERE (date BETWEEN '2015-12-30' AND '2015-12-25'
AND ((columB = '1' OR columC = '1') OR columD = '1')
AND (date BETWEEN '2015-11-1' AND '2015-11-30' AND (columE = '1'))
Arelを使って書くことができます。
Arelインスタンス生成
上記SQLの3行目に該当します。
book_query=Table.arel_table[:date].in((5.days.ago)..(2015-12-30))
OR条件
上記SQLの4行目のカッコ内に該当します。
Arel::Nodes::Node#orを使います。
book_query_or=Book.arel_table[:columB].eq(1)
book_query_or=book_query_or.or(Book.arel_table[:columC].eq(1))
book_query_or=book_query_or.or(Book.arel_table[:columD].eq(1))
AND条件
Arel::Nodes::Node#andを使います。
上記SQLの3行目と4行目のカッコ内をANDでつなぐ処理です。
book_query = book_query.and(book_query_or)
副問い合わせ
上記SQLの5行目に該当します。
条件式の作り方は上述のAND条件の作成方法と同じです。
副問い合わせにするためにgroupingを使用するのがポイントです。
Arel::Nodes::Node#groupingを使います。
book_subquery = Table.arel_table[:date].in((30.days.ago)..(2015-11-30))
book_subquery = book_subquery.and(Book.arel_table[:columE].eq(1))
book_subquery = book_subquery.grouping(book_subquery)
SQL実行
最後に今まで構築した条件式をクエリに投入して実行します。
(2015-01-01修正 副問い合わせ用のwhere句を追加)
Book.select("date,name").where(book_query).where('columE in (?)',Book.select("columE").where(book_subquery ))
このようにすれば、上記のSQLが発行できます。
それぞれのメソッドを組み合わせれば、なかなか複雑なクエリもかけそうです。
ただ、Rails4.2のArelでのOR条件作成では以下のような問題もあるようです。
http://qiita.com/joker1007/items/5c851526e73b3bc0273a