1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Rails5.1で順序依存なくSQLを組み立てる_ORクエリ編

Last updated at Posted at 2017-11-28

# Rails5.2で解決済み【追記】

Avoid duplicate clauses when using #or #29950
で修正されており、取り込まれた5.2で解決しました

前提

Rails5で順序依存なくSQLを組み立てる_scope編
本記事では 上記記事にある scope 内の unscoped に関する説明は省略します

概要

Rails5 で ActiveRecord に or クエリが導入されましたが適当に作ってると危ない
定義順に生成されるため「前条件はOR内」「後条件はOR外」になります

User.where(id: 1).where(id: 2).or(User.where(id: 3)).where(id: 4).to_sql
=> "SELECT `users`.* FROM `users` 
    WHERE (`users`.`id` = 1 AND `users`.`id` = 2 OR `users`.`id` = 3) AND `users`.`id` = 4"

これを 「2 or 3」 に限定したい場合はどうすれば良いでしょうか

後方定義

ORの挙動が周知されていればこれでも大丈夫でしょう

User.where(id: 2).or(User.where(id: 3)).where(id: 1).where(id: 4).to_sql
=> "SELECT `users`.* FROM `users` 
    WHERE (`users`.`id` = 2 OR `users`.`id` = 3) AND `users`.`id` = 1 AND `users`.`id` = 4"

しかし順序依存はスコープで発生すると大変です
せっかくスコープで定義したのに、使う時に内部実装を考慮しなくてはいけません

class User
  scope :hoge1,  -> { where(id: 1) }
  scope :hoge23, -> { where(id: 2).or(unscoped.where(id: 3)) }
  scope :hoge4,  -> { where(id: 4) }
end

User.hoge23.hoge1.hoge4.to_sql
=> "SELECT `users`.* FROM `users` 
    WHERE (`users`.`id` = 2 OR `users`.`id` = 3) AND `users`.`id` = 1 AND `users`.`id` = 4"

User.hoge1.hoge23.hoge4.to_sql
=> "SELECT `users`.* FROM `users` 
    WHERE (`users`.`id` = 1 AND `users`.`id` = 2 OR `users`.`id` = 3) AND `users`.`id` = 4"

merge

merge で範囲を限定させるとSQLが括弧で括られ独立します

User.where(id: 1).merge(User.where(id: 2).or(User.where(id: 3))).where(id: 4).to_sql
=> "SELECT `users`.* FROM `users` 
    WHERE `users`.`id` = 1 AND (`users`.`id` = 2 OR `users`.`id` = 3) AND `users`.`id` = 4"

順序依存がなくスコープを利用する際に、順番を意識する必要がありません

class User
  scope :hoge1,  -> { where(id: 1) }
  scope :hoge23, -> { merge(unscoped.where(id: 2).or(unscoped.where(id: 3))) }
  scope :hoge4,  -> { where(id: 4) }
end

User.hoge1.hoge23.hoge4.to_sql
=> "SELECT `users`.* FROM `users` 
    WHERE `users`.`id` = 1 AND (`users`.`id` = 2 OR `users`.`id` = 3) AND `users`.`id` = 4"

User.hoge23.hoge1.hoge4.to_sql
=> "SELECT `users`.* FROM `users` 
    WHERE (`users`.`id` = 2 OR `users`.`id` = 3) AND `users`.`id` = 1 AND `users`.`id` = 4"

参考

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?