0
0

More than 3 years have passed since last update.

【クエリ爆減】update_allで一定条件下の複数データをまとめて更新する【Ruby on Rails】

Last updated at Posted at 2020-08-09

入荷から一定期間経過した商品には割引(30日以上→500円・60日以上→1000円・90日以上→1500円)を適用する為、以下の様なロジックを書きました。

items = Item.where("created_at < ?", Time.zone.now - 30.days)
items.each do |item|
  created_at = item.created_at
  today = Time.zone.today
  three_month = 90.days
  two_month = 60.days
  one_month = 30.days

  if today >= created_at + three_month
    discount = -1500
  elsif today >= created_at + two_month && today < created_at + three_month
    discount = -1000
  elsif today >= created_at + one_month && today < created_at + two_month
    discount = -500
  end

  item.discount = discount
  item.save
end

しかし上記のコードでは、itemsの数の分処理が走ってしまう所謂 N+1問題 が発生し、処理速度やメモリに大きな影響を与えてしまいます。

そこで活躍するのがupdate_all
以下の様に条件をhashに纏めて配列に格納、各条件に当てはまるデータを纏めて更新します。

today = Time.zone.now
conditions = [
  { upper_limit: today - 30.days, lower_limit: today - 59.days, amount: -500 },
  { upper_limit: today - 60.days, lower_limit: today - 89.days, amount: -1000 },
  { upper_limit: today - 90.days, lower_limit: today - 365.years, amount: -1500 }, 
]
conditions.each do |condition|
  Item.
    where("created_at between ? and ?", condition[:lower_limit], condition[:upper_limit]).
    update_all(discount: condition[:amount])
end 

するとクエリは条件分のたった3つだけに収まります🎉

Item Update All (8.8ms)  UPDATE `items` SET `items`.`discount` = -500 WHERE (created_at between '2020-04-27 00:00:00' and '2020-05-26 00:00:00')
Item Update All (3.2ms)  UPDATE `items` SET `items`.`discount` = -1000 WHERE (created_at between '2020-03-28 00:00:00' and '2020-04-26 00:00:00')
Item Update All (19.4ms)  UPDATE `items` SET `items`.`discount` = -1500 WHERE (created_at between '1920-06-25 00:00:00' and '2020-03-27 00:00:00')

※ActiveRecordオブジェクトを経由しない更新方法のため、バリデーションやコールバックは実行されない点にご注意ください。

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