ActiveRecord:find_in_batches的替代方案?

时间:2015-05-28 14:58:47

标签: ruby-on-rails activerecord

我有一个加载数千个对象的查询,我希望使用find_in_batches来驯服它:

Car.includes(:member).where(:engine => "123").find_in_batches(batch_size: 500) ...

根据文档,我没有自定义排序顺序:http://www.rubydoc.info/docs/rails/4.0.0/ActiveRecord/Batches:find_in_batches

但是,我需要created_at DESC的自定义排序顺序。是否有另一种方法可以像在find_in_batches中那样以块的形式运行此查询,这样就不会立即在堆上存在这么多对象?

3 个答案:

答案 0 :(得分:2)

我一直在考虑解决这个问题(我是问这个问题的人)。有意义的是,find_in_batches不允许您拥有自定义订单,因为假设您按created_at DESC排序并指定batch_size为500.第一个循环从1-500开始,第二个循环开始如果在第二次循环发生之前,有人在表中插入一条新记录,那该怎么办?这将被置于查询结果的顶部,您的结果将向左移动1,您的第二个循环将重复。

你可以争辩说created_at ASC然后是安全的,但如果你的应用指定了created_at值,则无法保证。

更新:

我为这个问题写了一个宝石:https://github.com/EdmundMai/batched_query

自使用它以来,我的应用程序的平均内存已经HALVED。我强烈建议任何有类似问题的人来看看!如果你想要贡献!

答案 1 :(得分:1)

执行此操作的较慢的手动方式是执行以下操作:

count = Cars.includes(:member).where(:engine => "123").count
count = count/500
count += 1 if count%500 > 0
last_id = 0
while count > 0
    ids = Car.includes(:member).where("engine = "123" and id > ?", last_id).order(created_at: :desc).limit(500).ids #which plucks just the ids`   
    cars = Cars.find(ids)
    #cars.each or #cars.update_all
    #do your updating 
    last_id = ids.last
    count -= 1
end 

答案 2 :(得分:0)

你能想象find_in_batches如何对1M行或更多行进行排序?它将对每批次的所有行进行排序。

所以,我认为减少排序调用次数会更好。例如,对于批量大小等于500,您可以仅为N * 500行加载ID(包括排序),之后只需加载这些ID的批量对象。因此,这种方式应该减少查询到N次排序到DB。