rails数据库查询效率与多个关联

时间:2014-12-21 20:33:07

标签: ruby-on-rails database performance activerecord

我有一个拥有约1000个俱乐部和10,000个用户的数据库。我需要建立一个连续显示每个俱乐部的表格,其中包含与该俱乐部相关的用户数量,帖子和评论的列。我现在的查询是这样的(来自我的控制器):

@clubs.find_each do |club|
    @users.where(:club_id => club.club_id).find_each do |user|
         @numPostsByClub += user.posts.count
         @numCommentsByClub += user.comments.count

现在我想必须有一种更有效的方法来实现这一点,因为我的页面加载需要大约90秒。是否有更有效的方法来加载所有这些数据,或者可能是一种运行rake任务的方法(我只需要每天更新一次数据)来存储所有这些以便快速访问?

2 个答案:

答案 0 :(得分:1)

如果我理解正确,您需要俱乐部的评论总数和俱乐部的评论总数。

counter_cache 如果您想快速解答以下问题,可以为您提供帮助:

  1. 俱乐部拥有多少用户,或
  2. 用户有多少条评论,或
  3. 用户有多少帖子
  4. 但不是每个俱乐部的帖子或评论总数(尽管它会让你的生活更容易)。

    免责声明:在未创建数据库备份且未阅读完整答案的情况下,请勿开始将以下建议投入生产。


    要通过在club table中添加2列来更快地启动脚本:

    class AddCommentsCountToClubs < ActiveRecord::Migration
      def change
        add_column :clubs, :comments_count, :integer, default: 0
      end
    end
    
    class AddPostsCountToClubs < ActiveRecord::Migration
      def change
        add_column :clubs, :posts_count, :integer, default: 0
      end
    end
    

    对于每个俱乐部:

    1. 更新 comments_count 以包含属于俱乐部的用户添加的评论数
    2. 更新 posts_count 以包含属于俱乐部的用户添加的帖子数
    3. 要创建将更新计数器的rake任务,请添加包含以下内容的文件lib/tasks/update_clubs_counters.rake

      namespace :db do
        task :update_clubs_counters => :environment do
          Club.all.each do |club|
            club.update(comments_count: club.comments.count, posts_count: club.posts.count)
          end
        end
      end
      

      创建文件后运行bundle exec rake db:update_clubs_counters

      更新计数器的另一种方法是使用rails控制台并运行任务的内容(仅与更新相关的部分)


      然后,对于评论发布模型,添加2个回调来增加/减少每个相应俱乐部的计数器。

      为清楚起见,我将定义所涉及的所有模型及其之间的关系

      class Club < ActiveRecord::Base
        has_many :users
        has_many :comments, through: :users
        has_many :posts, through: :users
      end
      
      class User < ActiveRecord::Base
        belongs_to :club 
        has_many :posts
      end
      
      class Comment < ActiveRecord::Base
        belongs_to :user
      
        after_create :increment_club_comments_count
        after_destroy :decrement_club_comments_count     
      
        def increment_club_comments_count
          Club.increment_counter( :comments_count, user.club_id )
        end
      
        def decrement_club_comments_count
          Club.decrement_counter( :comments_count, user.club_id )
        end
      end
      
      class Post < ActiveRecord::Base
        belongs_to :user
      
        after_create :increment_club_posts_count
        after_destroy :decrement_club_posts_count
      
        def increment_club_posts_count
          Club.increment_counter( :posts_count, user.club_id )
        end
      
        def decrement_club_posts_count
          Club.decrement_counter( :posts_count, user.club_id )
        end
      end
      

      现在,每次添加/删除帖子/评论时,俱乐部表中的相应计数器都会递增/递减。

      您可以像这样简化控制器(只有一个查询,您将拥有所有数据):

      @clubs = Club.all # I recommend to use pagination and not to list all 1000 clubs at once
      

      在您的视图中,您只需显示您的计数器:

      <% @clubs.each do |club| %>
        <p>Comments Count: <%= club.comments_count %></p>
        <p>Posts Count: <%= club.posts_count %></p>     
      <% end %>
      

      您可以找到有关increment_counterdecrement_counter以及counter_cache with has_many :through

      的更多详细信息

答案 1 :(得分:0)

查看counter_cache关联的belongs_to选项。这会向父模型添加一个字段以缓存子对象的数量。创建或删除子对象时,将更新此字段以保持正确的计数。

可以在Ruby on Rails指南的Association Basics部分找到更多信息。

RailsCasts还有一个关于counter_cache选项的剧集,其中有一个示例实现:Example #23