Ruby on Rails更新100k记录的最佳方法

时间:2018-05-09 09:53:39

标签: ruby-on-rails ruby-on-rails-4

我遇到的情况是我必须以最有效的方式更新数据库中超过100k的记录请参阅下面的代码:

namespace :order do
  desc "update confirmed at field for Payments::Order"
  task set_confirmed_at: :environment do
    puts "==> Updating confirmed_at for orders starts ...".blue
    Payments::Order.find_each(batch_size: 10000) do |orders|
      order_action = orders.actions.where("sender LIKE ?", "%ConfirmJob%").first if orders.actions
      if !order_action.blank?
        orders.update_attribute(:confirmed_at, order_action.created_at)
        puts "order id = #{orders.id} has been updated.".green
      end
    end
    puts "== completed ==".blue
  end
end

在这里,我打破了每个批量大小10000的记录,然后尝试根据某些条件更新记录,这样任何人都可以建议我更有效地执行相同的任务。

提前谢谢!

2 个答案:

答案 0 :(得分:3)

您可以尝试update_all:

Payments::Order.joins(:actions).where(Payment::OrderAction.arel_table[:sender].matches("%ConfirmJob%")).update_all("confirmed_at = actions.created_at")

所以你的代码看起来像这样:

namespace :order do
  desc "update confirmed at field for Payments::Order"
  task set_confirmed_at: :environment do
    puts "==> Updating confirmed_at for orders starts ...".blue
    Payments::Order.joins(:actions).where(Payments::OrderAction.arel_table[:sender].matches("%ConfirmJob%")).update_all("confirmed_at = actions.created_at")
    puts "== completed ==".blue
  end
end

<强>更新: 我调查了一个问题,发现bulk update加入表是一个长期问题in rails 由于set部分使用字符串参数,因此我建议在那里添加子句。

namespace :order do
    desc "update confirmed at field for Payments::Order"
      task set_confirmed_at: :environment do
        puts "==> Updating confirmed_at for orders starts ...".blue
          Payments::Order.joins(:actions).
                where(Order::Action.arel_table[:sender].matches("%ConfirmJob%")).
                update_all("confirmed_at = actions.created_at FROM actions")
        puts "== completed ==".blue
      end
    end

答案 1 :(得分:0)

您正在执行Payments::Order.find_each,因此当您只想循环使用Payment::Order时,您的解决方案将针对每个actions.server like '%ConfirmJob%'进行循环,因此我将使用此解决方案:

Payments::Order
  .includes(:actions)
  .joins(:actions)
  .where("actions.server like '%?%'", "ConfirmJob")
  .find_each do |order|
    order_action = order.actions.first
    order.update!(confirmed_at: order_action.created_at)
end