1

アクティブなレコードで初めていくつかの N+1 クエリを最適化しようとしています。殺す必要があるのは 3 つあります - 2 は .includes 呼び出しで非常に簡単に実行できましたが、3 番目がまだ大量のクエリを呼び出している理由を一生理解できません。以下の関連コード - 誰かに提案があれば、本当に感謝しています。

コントローラ:

@enquiries = Comment.includes(:children).faqs_for_project(@project)

モデル;

def self.faqs_for_project(project)
  Comment.for_project_and_enquiries(project, project.enquiries).where(:published => true).order("created_at DESC")
end

(および関連するスコープ)

scope :for_project_and_enquiries, lambda{|p, qs| where('(commentable_type = ? and commentable_id = ?) or (commentable_type = ? and commentable_id IN (?))', "Project", p.id, "Enquiry", qs.collect{|q| q.id})}

見る:

...
= render :partial => 'comments/comment', :collection => @enquries
...

(そして部分的にその問題のある行)

...
= 'Read by ' + pluralize(comment.acknowledgers.count, 'lead')
...

コメントごとに 2 つの SQL クエリが呼び出されます。2 つのクエリは次のとおりです。

SQL (2.8ms)  SELECT COUNT(*) FROM "users" INNER JOIN "acknowledgements" ON "users".id = "acknowledgements".user_id WHERE (("acknowledgements".feedback_type = 'Comment') AND ("acknowledgements".feedback_id = 177621))
CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 1295 LIMIT 1

コントローラーの .includes に (:user, :acknowledgements) を追加すれば問題は解決すると思いましたが、何の効果もないようです。私が欠けているものについて何か提案があれば、本当に感謝しています

4

1 に答える 1

3

カウンターキャッシュとして列Commentを追加したいテーブルを信じています:acknowledgers_count

has_many :acknowledgers, ....., counter_cache: true

:acknowledgers_count列をcommentsテーブルに追加するには、移行を作成する必要があります。あとはRailsがやってくれます。

ActiveRecord::CounterCacheAPIの詳細については、こちらをご覧ください。

countメソッドcomment.acknowledgers.countは ActiveRecord でオーバーロードされ、最初にカウンター キャッシュ列が存在するかどうかを確認し、存在する場合は、データベースに再度アクセスすることなく、モデル(この場合はCommentモデル)から直接返します。

最後に、ごく最近、Gem 呼び出しBulletに関する優れたRailscastがありました。これは、これらのクエリの問題を特定し、解決策に導くのに役立ちます。カウンター キャッシュと N+1 クエリの両方をカバーします。

@ismaelga がこの回答へのコメントで指摘したように、一般的には、リレーション.sizeの代わりに呼び出す方が良い方法です。のソース.countチェックしてください:size

def size
  loaded? ? @records.length : count
end

リレーションが既にロードされている場合はそれを呼び出すだけlengthで、そうでない場合は を呼び出しますcount。これは、データベースが不必要に照会されないようにするための追加のチェックです。

于 2012-08-29T23:45:23.253 に答える