4

我正在查看官方 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 个,对吧?

4

3 回答 3

8

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

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

find_each一次并没有真正处理 1000 条记录。它加载 1000 条记录,然后处理每一条记录。

于 2013-03-17T03:57:15.053 回答
6

我建议从控制台运行它并查看 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
于 2013-03-17T04:29:32.213 回答
2

Ruby 有一个可爱的特性,叫做codeblocks。真正伟大的是,每个方法都假设 <silently> 接收一个代码块作为最后一个参数。可以动态检查代码块是否以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

希望能帮助到你。

于 2013-03-17T04:27:33.760 回答