Rails 4 で NOT な条件をもつ WHERE 句 が非常に書きやすくなりました。

Rails 4 なら NOT IN な SQL も簡単に書けます。

User.where.not( name: ["hoge","goro"] )

条件にリストを渡せばよいです。SQLは以下のようになります。

SELECT "users".* FROM "users"
  WHERE ("users"."name" NOT IN ('hoge', 'goro'))

サブクエリも使えます。これが便利すぎて困る。

query = User.select(:name)
User.where.not name: query
SELECT "users".* FROM "users"
  WHERE ("users"."name" NOT IN (SELECT name FROM "users"))

Rails 3 の話

そうはいっても、Rails 4 にすぐには更新できないプロジェクトがあるので、これに慣れてしまうと非常につらい。折角なのでメモしておく。

where に文字列を渡す

格好悪いけど、とりあえずごまかす場合はこれを使う。

User.where("name NOT IN ('hoge', 'goro')")

文字列で渡す。

? を使うとエスケープで泣きたくなるので、手軽には使えない。右辺も文字列で逃げます。

サブクエリを使うときは、こんな感じ。

query = User.select(:name)
User.where("name NOT IN (#{query.to_sql})")

SQLを直接書くしかない。複雑な query だとつらいので to_sql メソッドで逃げます。

squeel を使う

Rails 3 だと NOT なSQL を書きづらいので、 squeel をよく使っています。

squeel を使っていればこんな風に書けます。

User.where { name << ["hoge","mogu"] }

where に ブロックで引数が渡せるようになって << という演算子を使うと NOT IN になります。name はシンボルではないのも ポイントです。

ブロックで渡すので、スコープ内で name に値が束縛されているとそちらを参照してしまうので注意。

サブクエリも使える。素晴らしい。

query = User.select(:name)
User.where { name << query }

squeel を使うと LIKE やら OUTER JOIN やらもできます。便利。

arel の世界へ行く

arel で構築した sql を where に渡す方法がある。 squeel をインストールしたくない時に。

users = User.arel_table
User.where( users[:name].not_in(['hoge','mogu']) )

arel 自体あまり解説してる人がいないのでなかなか勉強しにくいのが欠点。 わかってくるとなんとなくでも書けます。

サブクエリもいけます。

query = User.select(:name)
users = User.arel_table
User.where( users[:name].not_in(query.arel) )

ActiveRecord::Relation はそのまま渡せませんが arel メソッドで変換可能です。

まとめ

Rails 4 の not 便利です。

squeel も良いです. 私は最初は仕組みがわからなくて、気持ち悪かったです。 ところが、 where メソッドの実装を見たら察しがつきました。 利用する場合は、一度目に目にすることをおすすめします。

Rails3 からの ActiveRecord::Relation になれると元の世界に帰れる気がしません。

この記事のタイトルに悩んだけど、「書きはじめた理由でいいや」と、結局考えるのを放棄した。