【问题标题】:Help optimizing ActiveRecord query (voting system)帮助优化 ActiveRecord 查询(投票系统)
【发布时间】:2010-09-27 21:26:08
【问题描述】:

我的投票系统有两个模型: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

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

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

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

【问题讨论】:

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


    【解决方案1】:

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

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

    在 rails 2 中,这应该可以工作:

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

    【讨论】:

    • 漂亮!这是我从中得出的 Rails 2.x 语法: Vote.find(:all, :group => "item_id", :order => "count(*) DESC", :limit => 1).first.item
    • 不要忘记 :include => :item (请参阅我的答案),否则当您调用 Vote#item 时,您将进行额外的查询。当您可以调用 Vote#first 时,还结合 :limit => 1 和 Array#first 似乎是多余的。
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      在 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
      

      【讨论】:

        【解决方案4】:

        处理此问题的一种简单方法是向项目添加投票计数字段,并在每次投票时更新该字段。 Rails 过去会自动为您执行此操作,但不确定在 2.x 和 3.0 中是否仍然如此。在任何情况下,使用观察者模式或在投票模型中放入“after_save”,您都可以很容易地做到这一点。

        那么您的查询非常简单,只需在查询中添加“VOTE_COUNT DESC”订单即可。

        【讨论】:

        • 谢谢,但我不想处理数据完整性问题。我宁愿在这个问题上坚持“一个版本的真相”。如果性能/扩展成为问题,也许我会实现这样的东西。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-02-06
        • 2011-05-06
        • 2011-03-01
        • 1970-01-01
        相关资源
        最近更新 更多