我尝试使用 ActiveRecord 的 find_each
方法运行大约 50,000 条记录的查询,但它似乎忽略了我的其他参数,如下所示:
Thing.active.order("created_at DESC").limit(50000).find_each {|t| puts t.id }
我不想停在 50,000 并按 created_at
排序,而是在整个数据集上执行的结果查询:
Thing Load (198.8ms) SELECT "things".* FROM "things" WHERE "things"."active" = 't' AND ("things"."id" > 373343) ORDER BY "things"."id" ASC LIMIT 1000
有没有办法获得与 find_each
类似的行为,但具有总最大限制并尊重我的排序标准?
最佳答案
The documentation说 find_each 和 find_in_batches 不保留排序顺序和限制,因为:
- 在 PK 上进行 ASC 排序用于进行批量排序。
- Limit 用于控制批量大小。
您可以像 @rorra 那样编写您自己的函数版本。但是当改变对象时你可能会遇到麻烦。例如,如果您按created_at排序并保存对象,它可能会在下一批中再次出现。同样,您可能会跳过对象,因为在执行查询以获取下一批时结果的顺序已更改。仅将该解决方案与只读对象一起使用。
现在我最关心的是我不想一次将 30000 多个对象加载到内存中。我关心的不是查询本身的执行时间。因此,我使用了执行原始查询但仅缓存 ID 的解决方案。然后它将 ID 数组划分为 block 并查询/创建每个 block 的对象。这样您就可以安全地改变对象,因为排序顺序保存在内存中。
这是一个与我所做的类似的最小示例:
batch_size = 512
ids = Thing.order('created_at DESC').pluck(:id) # Replace .order(:created_at) with your own scope
ids.each_slice(batch_size) do |chunk|
Thing.find(chunk, :order => "field(id, #{chunk.join(',')})").each do |thing|
# Do things with thing
end
end
此解决方案的权衡是:
- 执行完整的查询以获取 ID
- 所有 ID 的数组都保存在内存中
- 使用 MySQL 特定的 FIELD() 函数
希望这有帮助!
关于sql - ActiveRecord find_each 结合 limit 和 order,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15189937/