在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) 

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

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

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

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

像update_all这样的厕所是最好的选择……虽然我会保持我的hacky版本,以防你好奇:

您可以使用plain-ole 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)") 

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

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

 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或一些后台工作者。