【问题标题】:Ruby where with find_eachRuby where 与 find_each
【发布时间】:2013-03-17 12:29:32
【问题描述】:

我正在查看官方 Rails 文档,该文档展示了如何使用“find_each”方法。这是他们给出的一个例子

Person.where("age > 21").find_each do |person|
  person.party_all_night!
end

这一次处理 1000 条记录。但是,我仍然很困惑。这如何转化为 SQL?允许 Ruby 一次只能处理 1000 条记录的幕后发生了什么?

我有点困惑的原因是因为似乎 Person.where("age > 21") 会先执行,这将返回所有结果。

例如:

Person.where("age > 21").limit(10)

会先返回内存中的所有人,然后给你前 10 个,对吧?

【问题讨论】:

    标签: ruby-on-rails ruby activerecord


    【解决方案1】:

    Person.where("age > 21") 仅返回 ActiveRecord 关系。它不会返回所有结果。

    Person.where("age > 21").limit(10) 不会将所有模型加载到内存中,这会很糟糕且无法使用。它只加载 10 个。

    find_each 一次不会真正处理 1000 条记录。它加载 1000 条记录,然后处理每一条记录。

    【讨论】:

    • Person.where("age > 21") 如果您从控制台评估它,它可能看起来正在返回所有结果。 Person.where("age > 21").class 信息丰富。
    【解决方案2】:

    我建议从控制台运行它并查看 SQL 或阅读源代码。

    例如:

    User.find_each(:batch_size => 40) do |user| end
      User Load (1.0ms)  SELECT "users".* FROM "users" WHERE ("users"."id" >= 0) ORDER BY "users"."id" ASC LIMIT 40
      User Load (0.8ms)  SELECT "users".* FROM "users" WHERE ("users"."id" > 96) ORDER BY "users"."id" ASC LIMIT 40
      User Load (0.8ms)  SELECT "users".* FROM "users" WHERE ("users"."id" > 156) ORDER BY "users"."id" ASC LIMIT 40
      User Load (0.8ms)  SELECT "users".* FROM "users" WHERE ("users"."id" > 219) ORDER BY "users"."id" ASC LIMIT 40
      User Load (0.8ms)  SELECT "users".* FROM "users" WHERE ("users"."id" > 272) ORDER BY "users"."id" ASC LIMIT 40
      User Load (0.8ms)  SELECT "users".* FROM "users" WHERE ("users"."id" > 314) ORDER BY "users"."id" ASC LIMIT 40
      User Load (0.8ms)  SELECT "users".* FROM "users" WHERE ("users"."id" > 355) ORDER BY "users"."id" ASC LIMIT 40
    

    或者

    bundle show activerecord
    point your favorite code editor at that location and find the source
    

    【讨论】:

      【解决方案3】:

      Ruby 有一个可爱的特性,叫做 codeblocks每个 方法都假设 接收一个代码块作为最后一个参数,这让它真的很棒。可以动态检查代码块是否以if block_given? 给出。

      我猜你想知道为什么 Ruby 返回数据只带有 where 而只是 准备带有 where.whatever 链?好吧,ActiveRecord 隐式检查是否给出了代码块,或者执行底层 SQL 语句并遍历结果返回一个带有准备好但尚未执行的 SQL 语句的迭代器。后者将被延迟执行并按需缓存。例如,Array.each 使用了相同的做法。在幕后正在执行类似的操作:

      sql_prepare
      if block_given?
        @cache = sql_execute_and_cache
        @cache.each { yield @cache }
      end
      

      希望对你有帮助。

      【讨论】:

        猜你喜欢
        • 2015-07-12
        • 1970-01-01
        • 1970-01-01
        • 2017-04-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-05-16
        • 2017-03-17
        相关资源
        最近更新 更多