在rails中一次更新多个记录

时间:2011-03-22 16:38:57

标签: ruby-on-rails

在我正在构建的rails 2应用程序中,我需要更新具有特定属性的记录集合。我有一个命名范围来查找集合,但我必须迭代每个记录以更新属性。

,我将不得不进行一次查询来更新数千条记录

到目前为止我发现的内容类似于Model.find_by_sql("UPDATE products ...)

这感觉真的很年轻,但我用Google搜索并环顾四周,但没有找到答案。

为清楚起见,我所拥有的是:

ps = Product.last_day_of_freshness
ps.each { |p| p.update_attributes(:stale => true) }

我想要的是:

Product.last_day_of_freshness.update_attributes(:stale => true)

4 个答案:

答案 0 :(得分:46)

听起来你正在寻找ActiveRecord::Base.update_all - 来自文档:

  

如果所有记录与提供的一组条件匹配,则更新所有记录,也可以提供限制和订单。此方法构造单个SQL UPDATE语句并将其直接发送到数据库。它不会实例化所涉及的模型,也不会触发Active Record回调或验证。

Product.last_day_of_freshness.update_all(:stale => true)

实际上,由于这是rails 2.x(您没有指定) - named_scope链接可能不起作用,您可能需要将命名范围的条件作为update_all的第二个参数传递,而不是将其链接到产品范围的最后。

答案 1 :(得分:5)

答案 2 :(得分:4)

像update_all这样的Loos是最好的选择......虽然我会保留我的hacky版本,以防你感到好奇:

你可以使用普通的SQL来做你想做的事情:

ps = Product.last_day_of_freshness
ps_ids = ps.map(%:id).join(',') # local var just for readability
Product.connection.execute("UPDATE `products` SET `stale` = TRUE WHERE id in (#{ps_ids)")

请注意,这取决于数据库 - 您可能需要调整引用样式以适应。

答案 3 :(得分:0)

对于那些需要更新大量记录,一百万甚至更多记录的人来说,有一种很好的方法可以按批次更新记录。

product_ids = Product.last_day_of_freshness.pluck(:id)
iterations_size = product_ids.count / 5000

puts "Products to update #{product_ids.count}"

product_ids.each_slice(5000).with_index do |batch_ids, i|
  puts "step #{i} of iterations_size"
  Product.where(id: batch_ids).update_all(stale: true)
end

如果你的表有很多索引,它也会增加这些操作的时间,因为它需要重建它们。在我的情况下,当我为表中的所有记录运行update_all时,大约有两百万个,十二个索引,操作在一个多小时内没有完成。采用这种方法,开发环境大约需要20分钟,生产大约需要4分钟。当然,这应该在rake task或某个后台工作人员中。