帮助优化ActiveRecord查询(投票系统)

时间:2010-09-27 21:26:08

标签: sql mysql ruby-on-rails ruby activerecord

我有一个投票系统,有两个模型:Item(id,name)和Vote(id,item_id,user_id)。

这是我到目前为止的代码:

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

这有一些问题,主要是我检索所有的Item记录,然后使用Ruby来计算流行度。我几乎可以肯定有一个简单的解决方案,但我无法完全理解它。

我更愿意收集记录并在初始查询中运行计算。这样,我可以在查询中添加一个简单的:limit => 1(或LIMIT 1)。

任何帮助都会很棒 - 要么在所有ActiveRecord中重写,要么在原始SQl中重写。后者实际上会让我更清楚地了解我想要执行的查询的性质。

4 个答案:

答案 0 :(得分:3)

按项目ID对投票进行分组,按计数排序,然后取第一个项目。在rails 3中,代码为:

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

在rails 2中,这应该有效:

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

答案 1 :(得分:1)

sepp2k有正确的想法。如果您不使用Rails 3,则等效于:

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

答案 2 :(得分:0)

可能在ruby中有更好的方法可以做到这一点,但在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

答案 3 :(得分:0)

处理此问题的一种简单方法是向项目添加投票计数字段,并在每次投票时更新。 Rails曾经自动为你做这件事,但不确定它是否仍然是2.x和3.0的情况。在任何情况下使用Observer模式或者只是在投票模型中加入“after_save”就可以轻松完成。

然后只需在查询中添加“VOTE_COUNT DESC”订单,即可轻松查询。