4

「find_each」メソッドの使用方法を示す公式のRailsドキュメントを見ています。ここに彼らが与えた例があります

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 のかわいらしい機能があります。すべてのメソッドが、コードブロックを最後のパラメーターとして <黙って> 受け取ることを想定していることは、本当にすばらしいことです。コードブロックが で指定されたかどうかを動的にチェックする可能性がありますif block_given?

なぜRubyは単独でデータを返し、チェーンで準備するだけなのだろうか?コードブロックが指定されたかどうかを 暗黙的にチェックし、基になる SQL ステートメントを実行して結果を反復処理する、準備済みでまだ実行されていない SQL ステートメントを含む反復子を返します。後者は遅延実行され、オンデマンドでキャッシュされます。同じ練習が、たとえば、で使用されます。舞台裏では、次のようなことが行われています。wherewhere.whateverActiveRecordArray.each

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

それが役に立てば幸い。

于 2013-03-17T04:27:33.760 に答える