0
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 3 years have passed since last update.

railsでorするとき、atter = nil時に不要な sqlを節約する (手元では400倍早くなった)

Last updated at Posted at 2021-05-02

この記事は備忘録です

DB(例)

(id, first_name, last_name, email)
1	しのぶ	胡蝶	aaa@aa.com
2	杏寿郎	煉獄	bbb@bb.com
3	義勇	冨岡	ccc@cc.com
4	小芭内	伊黒	ddd@dd.com
5	天元	宇髄	eee@ee.com
6	蜜璃	甘露寺	fff@ff.com

モデル(例)

# User.rb
# id           :int      
# first_name   :string
# last_name    :string
# email        :string    

first_name_attr = "しのぶ"
last_name_attr  = nil
email_attr      = "fff@ff.com"

  
  User.where(first_name: first_name_attr)
      .or( User.where(last_name: last_name_attr) )
      .or( User.where(email: email_attr) )
           

この場合、last_nameのところ、いらないっすよね〜でもこれって、sql的に

SELECT COUNT(*) FROM `users` WHERE (`users`.`first_name` = 'しのぶ' OR `users`.`last_name` IS NULL ... )

となるんですけど、orなのに IS NULL って処理いらないなって。(whereでandとかになるならもちろんいる)
そして、この処理が結構重いので、railsの仕組みを使って軽くしよう、というのが今回の目的です。
(※ まあこれは実践的ではないかもしれないのでメンバーと要相談ということでお願いいたします。。)

結論

nilだと IS NULL の処理が走ってしまうので[]にする。

last_name = (last_name == nil ? [] : last_name)

nilの場合は[]に変換することで

SELECT COUNT(*) FROM `users` WHERE (`users`.`first_name` = 'しのぶ' OR 1=0  ... )

なって処理を飛ばすことができ、大幅に早くなります。

もし開発する上で、表示するとき遅いし、一旦仮で早くしたいとかいう事情がある場合には結構有効かなと思います。
ただし、上記にもあるように、
「実践的ではないかも。。」「ちょっと気持ち悪いな〜」

みたいな意見があり、この実装は見送られておりますのでご注意を。
おそらく、シンプルにifで分岐する方がいいのかなという意見です。
ただし、sqlは一つに纏まるような記述が必要そうです。

実践的にはこうなった (2021/05/06 更新)


    users = first_name.present? ? User.where(first_name: first_name_attr ) : User.none
    users = users.or(User.where(last_name: last_name_attr)) if last_name.present?
    users = users.or(User.where(email: email_attr)) if email.present?

モデル.noneで空のActiveRecord_Relationを取得

こうすることで、attrがない場合、sqlを無視することができるのでより無駄を減らせる。
これを応用し、現在携わるプロジェクトでは

1822.0ms => 3.9ms

これだけ改善した。
IS NULL がなくなるだけでこれだけ軽くなるんだなと改めて血肉になった思い。

sql改善例

改善前

User Load ( ms)  SELECT `users`.* FROM `users` WHERE ((`users`.`first_name` IS NULL  OR `users`.`last_name` = '冨岡' ) OR `users`.`email` IS NULL ) 

改善後

User Load ( ms)  SELECT `users`.* FROM `users` WHERE `users`.`last_name` = '冨岡'
0
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
0
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?