【问题标题】:Rails SQL COUNT N+1 inefficiencyRails SQL COUNT N+1 效率低下
【发布时间】:2011-01-16 19:50:47
【问题描述】:

我有一个博客。在我的索引页面上,我拉入所有博客文章。对于每篇博文,我都会计算那篇博文中的 cmets 数量。这会导致 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 行)?

【问题讨论】:

    标签: sql ruby-on-rails ruby


    【解决方案1】:

    您可以使用计数器缓存:http://guides.rubyonrails.org/association_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 的列。

    【讨论】:

    • 这正是我想要的!谢谢!
    【解决方案2】:

    一般来说,您需要这样的 SQL 查询:

      SELECT COUNT(*), blog_post_id
        FROM blog_comments
    GROUP BY blog_post_id;
    

    您可以使用它来创建一个从 blog_post_id 到 cmets 计数的哈希。

    【讨论】:

      【解决方案3】:

      你也可以看到这样的:

      BlogComment.group('blog_post_id').count
      

      在纯粹的 Rails 方式中。 :)

      【讨论】:

        【解决方案4】:

        您可以使用dase gem 或that video 中解释的技术之一。

        以 Dase 为例:

        Author.includes_count_of(:articles).each do |author| puts "#{author.name} has #{author.articles_count} articles" end

        【讨论】:

          【解决方案5】:

          这是一个 ActiveRecord 查询,用于搜索我的“site_access_log”表,其中包含对网站的 Web 访问。

          它从前 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 
          

          【讨论】:

            【解决方案6】:

            以下是如何清理嵌套的 N+1 并使其变得干净的分步说明:How to avoid N + 1 and keep your Ruby on Rails controller clean

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2012-08-14
              • 1970-01-01
              • 2011-01-18
              • 2016-12-04
              • 1970-01-01
              • 1970-01-01
              • 2015-02-14
              • 2015-10-06
              相关资源
              最近更新 更多