2010-09-27 34 views
2

我有一個投票系統有兩個模型:Item(id,name)和Vote(id,item_id,user_id)。幫助優化ActiveRecord查詢(投票系統)

這裏是我到目前爲止的代碼:

class Item < ActiveRecord::Base 
    has_many :votes 

    def self.most_popular 
    items = Item.all #where can I optimize here? 
    items.sort {|x,y| x.votes.length <=> y.votes.length}.first #so I don't need to do anything here? 
    end 
end 

有幾件事情不對的,主要是我檢索所有項目的記錄,然後使用Ruby來計算的普及。我幾乎可以肯定,這有一個簡單的解決方案,但我不能完全明白這一點。

我寧願收集記錄並在初始查詢中運行計算。這樣,我可以在查詢中添加一個簡單的:limit => 1(或LIMIT 1)。

任何幫助將是偉大的 - 重寫所有ActiveRecord甚至原始SQl。後者實際上會讓我更清楚地瞭解要執行的查詢的性質。

回答

3

集團,按次數排序,然後取第一個項目。在軌道3這個代碼是:

Vote.group(:item_id).order("count(*) DESC").first.item 

在軌道2,這應該工作:

Vote.all(:order => "count(*) DESC", :group => :item_id).first.item 
+1

美麗!這裏是我從這個派生的Rails 2.x語法:Vote.find(:all,:group =>「item_id」,:order =>「count(*)DESC」,:limit => 1).first.item – user94154 2010-09-27 21:45:32

+0

不要忘記:include =>:item(請參閱我的答案),否則當您打電話給Vote#項目時,您會做額外的查詢。如果您可以先調用Vote#,那麼還要組合:limit => 1和Array#首先看起來多餘。 – 2010-09-28 04:56:49

0

也許有更好的方式紅寶石做到這一點,但在SQL(MySQL的至少),你可以嘗試這樣的東西來獲得前10的排名:按商品編號票

SELECT i.id, i.name, COUNT(v.id) AS total_votes 
FROM Item i 
LEFT JOIN Vote v ON (i.id = v.item_id) 
GROUP BY i.id 
ORDER BY total_votes DESC 
LIMIT 10 
0

處理這一個簡單的方法是將計票字段添加到項目,並更新每次都有投票。 Rails曾經爲你自動做這件事,但不確定在2.x和3.0中是否仍然如此。在任何情況下,使用Observer模式或僅在Vote模型中輸入「after_save」就足夠了。

然後,您的查詢非常簡單,只需在查詢中添加「VOTE_COUNT DESC」命令即可。

+0

謝謝,但我不想處理數據完整性問題。我寧願堅持這一個「真相的一個版本」。如果性能/縮放成爲問題,也許我會實現這樣的事情。 – user94154 2010-09-27 21:50:02

1

sepp2k有正確的想法。如果您不使用Rails 3,則相當於:

Vote.first(:group => :item_id, :order => "count(*) DESC", :include => :item).item