ActiveRecord find_each结合限制和顺序

Avi*_*hai 64 sql activerecord ruby-on-rails

我正在尝试使用ActiveRecord的find_each方法运行大约50,000条记录的查询,但它似乎忽略了我的其他参数,如下所示:

Thing.active.order("created_at DESC").limit(50000).find_each {|t| puts t.id }
Run Code Online (Sandbox Code Playgroud)

而不是停留在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
Run Code Online (Sandbox Code Playgroud)

有没有办法获得类似的行为,find_each但总的最大限制和尊重我的排序标准?

Dir*_*urs 63

文档说find_each和find_in_batches不保留排序顺序和限制,因为:

  • 在PK上对ASC进行排序用于批量订购工作.
  • 限制用于控制批量大小.

您可以像@rorra一样编写自己的此函数版本.但是在改变对象时你会遇到麻烦.例如,如果按create_at排序并保存对象,则可能会在下一批次中再次出现.同样,您可能会跳过对象,因为在执行查询以获取下一批时,结果的顺序已更改.仅将该解决方案与只读对象一起使用.

现在我主要担心的是我不想一次将30000+个对象加载到内存中.我担心的不是查询本身的执行时间.因此,我使用了一个执行原始查询但只缓存ID的解决方案.然后它将ID数组分成块,并按块查询/创建对象.这样您就可以安全地改变对象,因为排序顺序保存在内存中.

这是一个类似于我所做的最小例子:

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
Run Code Online (Sandbox Code Playgroud)

该解决方案的权衡取舍是:

  • 执行完整查询以获取ID
  • 所有ID的数组都保存在内存中
  • 使用MySQL特定的FIELD()函数

希望这可以帮助!

  • 4.x文档说不支持限制.但最新的5.x文档似乎确实受到限制. (3认同)

ror*_*rra 24

find_each在引擎盖下使用 find_in_batches.

find_in_batches中所述,无法选择记录的顺序会自动设置为在主键("id ASC")上升序以使批量订购工作.

但是,应用标准,您可以做的是:

Thing.active.find_each(batch_size: 50000) { |t| puts t.id }
Run Code Online (Sandbox Code Playgroud)

关于限制,它尚未实现:https: //github.com/rails/rails/pull/5696


回答第二个问题,您可以自己创建逻辑:

total_records = 50000
batch = 1000
(0..(total_records - batch)).step(batch) do |i|
  puts Thing.active.order("created_at DESC").offset(i).limit(batch).to_sql
end
Run Code Online (Sandbox Code Playgroud)


Tho*_*emm 16

检索第ids一个并处理in_groups_of

ordered_photo_ids = Photo.order(likes_count: :desc).pluck(:id)

ordered_photo_ids.in_groups_of(1000, false).each do |photo_ids|
  photos = Photo.order(likes_count: :desc).where(id: photo_ids)

  # ...
end
Run Code Online (Sandbox Code Playgroud)

ORDER BY查询添加到内部调用也很重要.

  • 这将需要在一个查询中提取表的所有 ID,我不知道这对于较大的表是否可取(无论如何,这就是您使用 find_in_batches 的地方)。 (4认同)
  • 与公认的答案不同,这在PostgreSQL中有效。同样,保持答案简洁明了。 (2认同)

ras*_*som 7

Rails 6.1 添加了对、和中降序排列的支持find_eachfind_in_batchesin_batches