我正在尝试首次优化活动记录中的一些N + 1查询。有3个要杀 - 2个非常容易用.includes调用,但我不能为我的生活弄清楚为什么第三个仍然调用一堆查询。以下相关代码 - 如果有人有任何建议,我会非常感激。
控制器:
@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')
...
为每个评论调用两个SQL查询。这两个查询是:
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
我会想到将(:user,:acknowledgement)附加到控制器中.include会解决问题,但它似乎没有任何影响。如果有人对我所缺少的东西有任何建议,我会非常感激
答案 0 :(得分:3)
我相信您要在Comment
表中添加:acknowledgers_count
列作为计数器缓存
has_many :acknowledgers, ....., counter_cache: true
您需要创建迁移以将:acknowledgers_count
列添加到comments
表。 Rails应该处理其余的事情。
您可以详细了解ActiveRecord::CounterCache
api here 。
count
中的comment.acknowledgers.count
方法在ActiveRecord中重载,首先检查计数器缓存列是否存在,如果存在,则直接从模型返回(在本例中为Comment
模型),无需再次触摸数据库。
最后,最近有一个关于gem Railscast的精彩Bullet可以帮助您识别这些查询问题并指导您找到解决方案。它涵盖了计数器缓存和N + 1查询。
正如@ismaelga在对此答案的评论中指出的那样,在关系上调用.size
而不是.count
通常是更好的做法。查看size
def size
loaded? ? @records.length : count
end
:
length
如果关系已加载,则只需在其上调用count
,否则会调用{{1}}。这是一个额外的检查,试图阻止数据库被不必要地查询。