15

これは、1年前の質問と変更に関連しています。

sqlite3が利用可能であれば、箱から出してすぐに機能するはずの質問の例を示します:https ://github.com/cairo140/rails-eager-loading-counts-demo

インストール手順(メインブランチ用)

git clone git://github.com/cairo140/rails-eager-loading-counts-demo.git
cd rails-eager-loading-counts-demo
rails s

私はリポジトリにもっと完全な記述を持っていますが、私の一般的な質問はこれです。

全体的なデータベースクエリを最小限に抑える方法で、Railsの負荷カウントを熱心に行うにはどうすればよいですか?

ActiveRelationにその関連付けを含めたにもかかわらず、関連付けでn+1使用するたびに問題が発生します。回避策はを使用することですが、これは、呼び出されているオブジェクトがすでにロードされている場合にのみうまく機能します。もちろん、Railsの内部ですでに行われていることと重複していると思われます。また、使用に関する問題は、関連付けが最初にロードされておらず、カウントが必要なすべてである場合に、不幸なオーバーロードが発生することです。#count#includes(:associated)#length#length

READMEから:

すでにロードされているposts配列(付録を参照)で#lengthを実行することでこの問題を回避できますが、カウントもすぐに利用できるようにしておくと便利です。一貫性が高いだけでなく、投稿をロードする必要がないアクセスパスを提供します。たとえば、何があってもカウントを表示するパーシャルがあるが、半分の時間、パーシャルは投稿がロードされた状態で呼び出され、半分の時間はロードされていない場合、次のシナリオに直面します。

  • 使用する#count
    • 投稿がすでに読み込まれている場合のnCOUNTスタイルのクエリ
    • COUNT投稿がまだロードされていない場合のnスタイルのクエリ
  • 使用する#length
    • 投稿がすでに読み込まれている場合、追加のクエリはゼロです
    • *投稿がまだロードされていない場合のnスタイルのクエリ

これらの2つの選択肢の間には、支配的な選択肢はありません。ただし、次のシナリオを実行できるように、#countを修正して#lengthに延期するか、バックグラウンドで保存されている他の方法で保存されている長さにアクセスすると便利です。

  • 改訂版を使用#count
    • 投稿がすでに読み込まれている場合、追加のクエリはゼロです
    • COUNT投稿がまだロードされていない場合のnスタイルのクエリ

では、ここでの正しいアプローチは何ですか?私が見落としているものはありますか(非常に、非常に可能性が高いです)?

4

4 に答える 4

9

@apneadiving が示唆したように、counter_cache は、レコードが追加または削除されるとカウンター列が自動的に更新されるため、うまく機能します。したがって、親オブジェクトをロードすると、他のテーブルにアクセスする必要なく、カウントがオブジェクトに含まれます。

ただし、何らかの理由でそのアプローチが気に入らない場合は、次のようにすることができます。

Post.find(:all,
          :select => "posts.*, count(comments.id) `comments_count`",
          :joins  => "left join comments on comments.post_id = posts.id")
于 2011-02-19T07:50:38.060 に答える
3

Zubin の代替アプローチ:

Post.select('posts.*, count(comments.id) `comments_count`').joins(:comments).group('posts.id')
于 2014-02-16T07:35:18.650 に答える
2

この種の機能を実装する最良の方法は、必要な個別のモデルおよび子カウント オブジェクトのSQL ビュー (参照:ここおよびここ) を作成することであると思われます。および関連する ActiveRecord モデル。

オブジェクトのすべての元のメソッドと、場合によってはそれらの関連付けの一部を保持するために、非常に賢く、元のモデルでサブクラス化を組み合わせて使用​​できる場合があります。set_table_name :sql_view_name

たとえば、上記の @Zubin の回答のように、「Post.has_many :comments」を例に追加するとします。次に、次のことができる場合があります。

   class CreatePostsWithCommentsCountsView < ActiveRecord::Migration
      def self.up
        #Create SQL View called posts_with_comments_counts which maps over 
        # select posts.*, count(comments.id) as comments_count from posts 
        #   left outer join comments on comments.post_id = posts.id 
        #   group by posts.id
        # (As zubin pointed out above.) 
        #*Except* this is in SQL so perhaps we'll be able to do further 
        # reducing queries against it *as though it were any other table.*
      end    
   end

   class PostWithCommentsCount < Post         #Here there be cleverness.
                                              #The class definition sets up PWCC 
                                              # with all the regular methods of 
                                              # Post (pointing to the posts table
                                              # due to Rails' STI facility.)

    set_table_name :posts_with_comment_counts #But then we point it to the 
                                              # SQL view instead.
                                              #If you don't really care about
                                              # the methods of Post being in PWCC
                                              # then you could just make it a 
                                              # normal subclass of AR::Base.
   end

   PostWithCommentsCount.all(:include => :user)  #Obviously, this sort of "upward
     # looking" include is best used in big lists like "latest posts" rather than
     # "These posts for this user." But hopefully it illustrates the improved 
     # activerecordiness of this style of solution.
   PostWithCommentsCount.all(:include => :comments) #And I'm pretty sure you 
     # should be able to do this without issue as well. And it _should_ only be 
     # the two queries.
于 2011-02-27T19:17:39.423 に答える
2

ActiveRecord にメソッドを追加する小さな gem をセットアップしましたincludes_count。これは、SELECT COUNT を使用して関連付け内のレコード数を取得し、高価な JOIN に頼ることなく (場合によっては) 行います。

https://github.com/manastech/includes-countを参照してください

それが役に立てば幸い!

于 2012-02-10T20:36:59.350 に答える