9阅网

您现在的位置是:首页 > 知识 > 正文

知识

sql - Rails SQL COUNT N+1效率低下

admin2022-11-06知识16

我有一个博客。在我的索引页上,我把所有的博客文章拉进来。对于每篇博文,我都会计算该文章的评论数。这就导致了一个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控制器的干净。