Ruby on rails ActiveRecord:批量查找\u的替代方法?

Ruby on rails ActiveRecord:批量查找\u的替代方法?,ruby-on-rails,activerecord,Ruby On Rails,Activerecord,我有一个加载数千个对象的查询,我想通过使用find\u in\u batches: Car.includes(:member)。其中(:engine=>“123”)。批量查找(批量大小:500). 根据文档,我不能有自定义排序顺序: 但是,我需要在DESC处创建一个自定义排序顺序。有没有另一种方法可以像在find_in_batches中那样分块运行此查询,这样就不会有那么多对象同时存在于堆中?您能想象find_in_batches和排序将如何在1M行或更多行上工作吗?它将每批对所有行进行排序 因

我有一个加载数千个对象的查询,我想通过使用
find\u in\u batches

Car.includes(:member)。其中(:engine=>“123”)。批量查找(批量大小:500).

根据文档,我不能有自定义排序顺序:


但是,我需要在DESC处创建一个自定义排序顺序
。有没有另一种方法可以像在find_in_batches中那样分块运行此查询,这样就不会有那么多对象同时存在于堆中?

您能想象
find_in_batches
和排序将如何在1M行或更多行上工作吗?它将每批对所有行进行排序


因此,我认为最好减少排序调用的数量。例如,对于批量大小等于500的情况,您可以仅加载N*500行的ID(包括排序),然后根据这些ID加载对象批。因此,这种方法应该能够在N次内减少对DB进行排序的have查询

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

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 

嗯,我一直在考虑解决这个问题的办法(我就是提出这个问题的人)。可以理解的是,
find_in_batches
不允许您拥有自定义订单,因为假设您按DESC
创建的
排序,并指定批次大小为500。第一个循环从1-500开始,第二个循环从501-1000开始,等等。如果在第二个循环发生之前,有人在表中插入了一条新记录,该怎么办?这将放在查询结果的顶部,结果将向左移动1,第二个循环将重复

你可能会争辩说在ASC创建的
是安全的,但是如果你的应用程序指定了一个创建的\u at值,就不能保证安全

更新:

我为这个问题写了一个gem:


自从使用它以来,我的应用程序的平均内存减少了一半。我强烈建议任何有类似问题的人去看看!如果你愿意的话,贡献自己的力量

好问题。你看了吗?只有大约5000次下载,因此可能需要一些工作。如果你没有得到一个更好的答案并最终尝试了这个方法,那么听听它是如何工作的将是很有趣的。你是在尝试对原始查询进行自定义排序(因此你每次按特定顺序抽取500个),还是对返回的结果进行排序(仅对500个进行排序)?对原始查询进行排序,不仅仅是批处理如果您想对原始查询进行排序,我相信在where之后添加.order(“created_at DESC”)可以对原始查询进行排序。尝试过了,没有成功。它还打印警告
忽略范围订单和限制,强制为批次订单和批次大小
它默认为在
id ASC上排序
。这有什么不同吗?您可以在数据库列上定义一个索引,这样数据库就不会对每个请求进行排序。正如上面的一条评论中所提到的,如果您正在寻找分页,那么就有一个gem。如果您正在寻找一种方法来迭代大型数据集以更新或提取查询,那么只要您跟踪最后提取的
id
,类似的方法就行了。它只需进行多个调用即可,但第二个调用至少基于id索引进行拉取。