Leon's Blogging

Coding blogging for hackers.

Ruby on Rails - Counter_cache

| Comments

通常要計算 has_many 關聯資料的數量時,就會用以下方式。

1
@book.pages.size

但這樣就會產生很多SQL count查詢,造成效能上的問題

1
2
3
4
SELECT * FROM `pages` LIMIT 5 OFFSET 0
SELECT count(*) AS count_all FROM `pages` WHERE (`pages`.book_id = 1 )
SELECT count(*) AS count_all FROM `pages` WHERE (`pages`.book_id = 2 )
SELECT count(*) AS count_all FROM `pages` WHERE (`pages`.book_id = 3 )

因此可以在 Book Model 上新增一個欄位,pages_count,用來記錄該 book 的 page 數量,之後撈資料,就直接撈這個數字就可以了。

1
2
3
4
5
def change
  add_column :books, :pages_count, :integer, default: 0
end

#型態是 integer,記得設定 default 值

接著編輯兩邊的 model,Rails 內建有 counter_cache helper,可以直接去做計算!

1
2
3
4
5
6
7
8
9
class Book < ActiveRecord::Base
  has_many :pages
end

class Page < ActiveRecord::Base
  belongs_to :book, :counter_cache => true
end

#設定成 ture,就會自動去找 pages_count 欄位,若要指定欄位則是 counter_cache: :count_of_pages

這樣之後下 @book.pages.size 的指令時 Rails 就會預設去找 pages_count 的欄位,不用重新下 SQL指令計算。

也可以直接下 @book.pages_count

最後注意,必須要用 createdestroy 才會 call back 更改數字,delete 則不會。

另外如果想要將數字重新計算可以用以下,可以直接在 rails c 裡面跑 也可以在新增 counter 的 migration 裡面直接加上去,在 db:migrat 的時候就會刷新了

1
2
3
Page.pluck(:id).each do |i|
  Page.reset_counters(i, :books)
end

官方文件: Guides Guides 中文 pluck reset_counters

Comments