在我正在构建的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)
答案 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)
您是否尝试过使用update_all
?
http://api.rubyonrails.org/classes/ActiveRecord/Relation.html#method-i-update_all
答案 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
或某个后台工作人员中。