10/30/2006

從 SQL 看 ActiveRecord 實做方式 (2) :Count

自從上次寫了 從 SQL 看 ActiveRecord 實做方式 ,我養成了寫完程式,會順便看 SQL 的好習慣。今天早上我花點時間來測試一下,計算資料庫數量的多寡的重要函式 Count 。之前我要寫數量多寡都是直接用 .size 來寫。像是找出 User 裡面名字叫做 a 的所有資料數量
User.find_all_by_nickname('a').size
結果出現的 SQL 是
User Load (0.000444) SELECT * FROM users WHERE (users.`nickname` = 'a' )
媽呀,真是刺激。也就是說,他會將所有的 nickname 為 a 的資料全部傳出來(1),塞給一個 array(2) ,然後計算 array 的數量大小。假設資料數量一大起來,1的傳輸時間會大幅度的增加,2的 array 所佔用的記憶體也會暴增。

再來,我來測試一下,關聯性 has_many 遇到計算數量時候的作法
a = User.find(1)
a.blogs.size
出現的 SQL 是
User Load (0.000339) SELECT * FROM users WHERE (users.id = 1) LIMIT 1
Blog Load (0.000320) SELECT * FROM blogs WHERE (blogs.user_id = 1)
媽呀,真是跟上面一樣刺激,會遇到的問題也跟上面一樣麻煩。也就是說,用 .size 的作法完全不可取。

我們換個方式,我看到了關聯性 has_many 支援一個 function ,就是在has_many 後面加入一個 _count
a = User.find(1)
a.blogs_count
出現的 SQL 是
User Load (0.000339) SELECT * FROM users WHERE (users.id = 1) LIMIT 1
Blog Load (0.000320) SELECT * FROM blogs WHERE (blogs.user_id = 1)
天呀,居然跟 .size 一樣,真是刺激的作法(有寫跟沒寫一樣)。

這個時候,臉上不禁出現三條線,那麼簡單的事情 Ruby on Rails 會沒考慮到?怎麼可能,我通常遇到這個問題時,會先 Chanllege 自己一下『應該是我不熟悉,而是沒有這個東西』。這時拿出居家旅行必備良書 Agile Web Programming with Rails 出來拜一拜,發現到如果我們使用
User.count(["nickname = ?" , "a"])
出現的 SQL 是
SQL (0.000324) SELECT count(*) AS count_all FROM users WHERE (nickname = 'a')
而關聯性資料庫
a.blogs.count
出現的 SQL 是
SQL (0.000296) SELECT count(*) AS count_all FROM blogs WHERE (blogs.user_id = 1)
拉拉,就是這個就是這個。 在 DB 處理 select count(*) 本來就會比 select * 來得快而且省記憶體。並且在資料庫傳輸到程式語言裡面只傳一個數字遠比傳一組 array 來得快。傳到程式語言裡面變數佔用的記憶體也有天壤之別。

結論是請大量使用 .count ,而請不要使用 _count ,或是 .size 來計算資料庫數量的大小。

沒有留言: