sql - Rails SQL COUNT N+1效率低下
我有一个博客。在我的索引页上,我把所有的博客文章拉进来。对于每篇博文,我都会计算该文章的评论数。这就导致了一个N+1的问题。我的查询内容如下。
SELECT "blog_posts".* FROM "blog_posts" WHERE ("blog_posts"."published" = 't') ORDER BY published_at DESC
SELECT "users".* FROM "users" WHERE ("users"."id" IN (1, 2, 3))
SELECT COUNT(*) FROM "blog_comments" WHERE ("blog_comments".blog_post_id = 10)
SELECT COUNT(*) FROM "blog_comments" WHERE ("blog_comments".blog_post_id = 9)
SELECT COUNT(*) FROM "blog_comments" WHERE ("blog_comments".blog_post_id = 8)
SELECT COUNT(*) FROM "blog_comments" WHERE ("blog_comments".blog_post_id = 2)
SELECT COUNT(*) FROM "blog_comments" WHERE ("blog_comments".blog_post_id = 7)
在Rails中,有没有一种方法可以让我以包含用户的方式包含COUNT(SQL行2)?
【回答】:
你可以使用计数器缓存。http:/guides.rubyonrails.orgassociation_basics.html#counter_cache。
"通过这个声明,Rails将保持缓存值的更新,然后响应size方法返回该值。"
class BlogPost < ActiveRecord::Base
has_many :blog_comments
end
class BlogComment < ActiveRecord::Base
belongs_to :blog_post, :counter_cache => true
end
博客文章会有一列名为 blog_comments_count
.
在一般情况下,你需要一个SQL查询,如。
SELECT COUNT(*), blog_post_id
FROM blog_comments
GROUP BY blog_post_id;
你可以用它来创建一个哈希 从blog_post_id到评论数。
【回答】:你也可以看到这样的东西。
BlogComment.group('blog_post_id').count
在纯粹的Rails方式。)
【回答】:Dase的例子。
Author.includes_count_of(:articles).each do |author|
puts "#{author.name} has #{author.articles_count} articles"
end
这是一个ActiveRecord查询,用来搜索我的 "site_access_log "表,包括对一个网站的访问。
它从前15条记录中选择 "remote_addr "字段,以及该IP的计数,按照计数降序排列,然后是具有相同count_number的IP号的升序。
我使用的是Postgres,它能理解IPv4的数字,所以我把这个字段转成了 inet
类型来允许正确的按值排序,而不是按ASCII值排序。如果你的数据库不支持inet值,你总是可以使用Ruby的Socket或IPSocket库从IP转换到inet,然后对检索结果进行排序。
@remote_addr_results = SiteAccessLog.all(
:select => 'remote_addr, count(remote_addr) as remote_addr_count',
:group => :remote_addr,
:order => 'remote_addr_count desc, cast(remote_addr as inet)',
:limit => 15
)
puts @remote_addr_results.map{ |r| r.remote_addr_count << ' : ' << r.remote_addr }
>> 985 : 68.228.61.183
>> 572 : 205.203.134.197
>> 500 : 68.32.220.153
>> 460 : 72.200.64.128
>> 281 : 24.121.196.194
>> 262 : 99.91.9.155
>> 241 : 68.99.237.178
>> 213 : 68.99.119.137
>> 208 : 70.167.157.162
>> 204 : 201.165.6.2
>> 164 : 72.201.233.147
>> 155 : 75.245.177.106
>> 150 : 97.123.246.154
>> 149 : 201.165.190.98
>> 145 : 74.37.165.220
生成的SQL看起来是这样的。
SELECT remote_addr, count(remote_addr) as remote_addr_count
FROM "site_access_logs"
GROUP BY remote_addr
ORDER BY remote_addr_count desc, cast(remote_addr as inet)
LIMIT 15
【回答】:下面是一步步清理嵌套N+1的方法,并使之干净。如何避免N+1,并保持你的Ruby on Rails控制器的干净。