2014-12-21 45 views
1

我有一個數據庫約1000俱樂部和10,000用戶。我需要建立一個顯示每個俱樂部連續的表格,其中包含與該俱樂部相關的用戶數量,帖子和評論的列。查詢我現在是這樣的(從我的控制器):rails數據庫查詢效率與多個協會

@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任務的方式(我只需要每天更新一次數據),將所有這些數據存儲起來以便快速訪問?

回答

1

如果我理解正確的話,你希望通過俱樂部的意見總數和由俱樂部意見的總數。

counter_cache會幫助你,如果你想有這樣的問題快速解答:

  1. 有多少用戶在舞廳裏有,或
  2. 多少評論的用戶,或
  3. 如何很多帖子用戶有

但是沒有與每個俱樂部的帖子或評論的總數(雖然它會讓你的生活有點e asier)。

免責聲明:如果不創建數據庫的備份並且沒有閱讀完整答案,請不要開始將以下建議用於生產。


使你的腳本更快的啓動加入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包含屬於俱樂部

要創建一個耙的任務,將更新的計數器由用戶添加的職位數目,添加文件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

後的另一種方式來更新你的計數器是使用軌道控制檯和運行任務(只有部分releva的內容NT才能更新)


然後,對於註釋郵政模型,加入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_countercounter_cache with has_many :through

+0

感謝這樣一個徹底的解釋,這很有道理。我相信現在一切正常(我沒有得到任何錯誤,並且我的schema.rb看起來已經正確更新),但我似乎無法填充這兩個新列。如果我把你在Rake任務中建議的代碼放到了試圖運行的代碼中,我會得到這個錯誤「NameError:未初始化的常量俱樂部」。如果我將該代碼放入遷移文件本身,則不會有任何反應。我是否需要在rake任務中以某種方式明確定義Club? (同樣,我將不能提供高達) – volx757

+0

嗨,我更新了這篇文章,以幫助您完成耙子任務。 – cristian

0

檢出您的belongs_to關聯的counter_cache選項。這會向父模型添加一個字段來緩存子對象的數量。當創建或刪除子對象時,該字段會更新以保持正確的計數。

有關更多信息,請參閱Ruby on Rails指南的Association Basics部分。

RailsCasts也有關於counter_cache選擇一個小插曲,示例實現的:Example #23

+0

謝謝,我做到了這一點跟着鋼軌投球,並且有一個表現改善,但還不夠(現在需要30秒,而不是60-90)。我想我的理想方法是從數據庫中進行某種緩存所有相關數據。這可以通過運行rake任務將數據拉入變量,然後從我的控制器訪問該數據來完成? (也抱歉,我不能upvote呢) – volx757

+0

你也應該看看通過SQL總結緩存計數。看看這個答案的更多信息:http://stackoverflow.com/questions/16052191/rails-has-many-through-sum-attribute-on-child-objects-sql-toughy 在你的例子中,你總結counter_cache字段,即'@ club.users.sum(:posts_count)' – NolanDC

+0

嘗試添加'includes' - '@ clubs.includes(:users)',它會將所有DB請求減少爲2個SQL查詢。 –