LoginSignup
4
5

More than 5 years have passed since last update.

逆引きwhere.or

Posted at

ActiveRecord::QueryMethods.#orの使い方が理解しにくいので、整理してみました。

ActiveRecordのwhereの引数に文字列でWHERE句を渡せば、なんでも表現できますが、シンタックスハイライトが効かなかったり、静的解析を行えないので、できるだけSQLを文字列で記述したくないです。

where.or 基本形

Model.where(a).or(Model.where(b))
# SELECT * FROM "users" WHERE (a OR b)

実際に動かしてみると、こんな感じ

User.where(name: "taro").or(User.where(email: "taro@example.com"))
# SELECT  "users".* FROM "users" WHERE ("users"."name" = $1 OR "users"."email" = $2)

以降では、わかりやすくするために前者の表記で記載するので、適宜読み替えてください。

チェーン形式

Model.where(a).or(Model.where(b)).or(Model.where(c))
# SELECT * FROM models WHERE ((a OR b) OR c)
# つまり、 SELECT * FROM models WHERE (a OR b OR c)

単純に全ての条件をorなら、チェーンで繋いでいけばいいです。

andと併用

(a && b) || c

Model.where(a).where(b).or(Model.where(c))
# SELECT * FROM models WHERE (a AND b OR c)

andorが混在すると、演算子の優先度と同じように&&が優先されるため、単純に繋げると、orは後から評価されます。

a && (b || c)

Model.where(b).or(Model.where(c)).where(a)
# SELECT * FROM models WHERE (b OR c) AND a

orを優先したい場合、ActiveRecordでは、評価の優先順をチェーンメソッドの並び順で表現するようです。
自分は、これをよく使う場面があります。例えば、公開終了日を表すend_atnullを許可する場合、公開中のデータを取得するにはWHERE start_at <= :time AND (:time <= end_at OR end_at IS NULL);といったことが行いたくなります。

(a && b) || (c && d) || (e && f)

最後に、こんなことがしたい場合は、以下のようになります。
ここまでくると、SQL直書きの方がマシですね。。。

Model.where(a).where(b).or(User.where(c).where(d).or(User.where(e).where(f)))
# SELECT  "users".* FROM "users" WHERE (a AND b OR (c AND d OR e AND f)) 

他にもorの表現パターンが見つかったら追記していきたいと思います。

4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5