sql - ActiveRecord find_each 结合 limit 和 order

标签 sql ruby-on-rails activerecord

我尝试使用 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/

相关文章:

ruby-on-rails - 使用 secure_random stub rspec 中的随机值

ruby-on-rails - 卡住关联对象

mysql - 如何获取 rails 事件记录属性对应的 SQL 值

ruby-on-rails - 为 mongoid 哈希字段设置默认哈希键

sql - DISTINCT 与 PARTITION BY 对比 GROUPBY

SQL 逻辑键

sql - 仅按连接表最大值选择行

ruby-on-rails - 在 Rails 中缓存 yaml 文件

mysql - ActiveRecord 从类中选择一个属性

sql - 具有分组依据的oracle滞后函数