Mot*_*ars 31 sql ruby-on-rails
我需要批量更新数千条记录,我想分批处理更新.首先,我试过:
Foo.where(bar: 'bar').find_in_batches.update_all(bar: 'baz')
Run Code Online (Sandbox Code Playgroud)
...我希望生成SQL,例如:
"UPDATE foo SET bar = 'baz' where bar='bar' AND id > (whatever id is passed in by find_in_batches)"
Run Code Online (Sandbox Code Playgroud)
这不起作用,因为find_in_batches返回一个数组,而update_all需要一个ActiveRecord关系.
这是我接下来尝试的:
Foo.where(bar: 'bar').select('id').find_in_batches do |foos|
ids = foos.map(&:id)
Foo.where(id: ids).update_all(bar: 'baz')
end
Run Code Online (Sandbox Code Playgroud)
这是有效的,但它显然运行一个选择后跟更新,而不是基于我的'where'条件的单个更新.有没有办法清理它,以便选择和更新不必是单独的查询?
dla*_*kty 59
在Rails 5中,有一种新的方便方法ActiveRecord::Relation#in_batches来解决这个问题:
Foo.in_batches.update_all(bar: 'baz')
Run Code Online (Sandbox Code Playgroud)
检查文档以获取详细信
pdo*_*obb 11
我也很惊讶,没有一种更简单的方法可以做到这一点......但我确实提出了这种方法:
batch_size = 1000
0.step(Foo.count, batch_size).each do |offset|
Foo.where(bar: 'bar').order(:id)
.offset(offset)
.limit(batch_size)
.update_all(bar: 'baz')
end
Run Code Online (Sandbox Code Playgroud)
基本上这将:
0并Foo.count逐步调整batch_size.例如,如果Foo.count == 10500你得到:[0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000]id,并限制为batch_size.batch_size"索引"大于的记录offset.这基本上是在生成的SQL中执行您所说的希望的手动方式.太糟糕了,它不能仅仅通过标准库方法以这种方式完成...虽然我确信你可以创建自己的一个.
这是迟了2年,但这里的答案是:a)对于大型数据集来说非常慢; b)忽略内置轨道功能(http://api.rubyonrails.org/classes/ActiveRecord/Batches.html).
随着偏移值的增加,它将根据您的数据库服务器进行序列扫描,直到它到达您的块,然后提取数据进行处理.随着您的偏移量达到数百万,这将非常缓慢.
使用"find_each"迭代器方法:
Foo.where(a: b).find_each do |bar|
bar.x = y
bar.save
end
Run Code Online (Sandbox Code Playgroud)
这具有每次保存运行模型回调的额外好处.如果您不关心回调,请尝试:
Foo.where(a: b).find_in_batches do |array_of_foo|
ids = array_of_foo.collect &:id
Foo.where(id: ids).update_all(x: y)
end
Run Code Online (Sandbox Code Playgroud)