获取“投票得分”的SQL查询

时间:2009-09-13 01:27:04

标签: sql ruby-on-rails postgresql

我的votes表格如下:

id: integer
vote: boolean
voteable_id: integer
voteable_type: string
voter_id: integer
voter_type: string

vote列确定该行是代表“向上投票”(vote = true)还是“向下投票”(vote = false)。

voteable_type是被投票的内容的类,voteable_id是被投票的内容的ID,voter_type是选民的类,{{1}是投票人的身份。

我需要的是通过“投票得分”按降序排列顶部 n voter_id的查询,其中“投票得分”定义为(该职位的票数) - (该职位的票数)。

如果您的解决方案不需要我诉诸posts(我在Rails工作),那么奖励积分

如果您的解决方案在SQLite和PostgreSQL中以相​​同的方式工作,那么会获得更多奖励(尽管在PostgreSQL中工作更重要)。

3 个答案:

答案 0 :(得分:3)

通常,您可以使用带有casesum语句来执行此操作:

select
    voteable_id,
    sum(case
        when vote then 1
        else -1
    end) as vote_score
from
    votes
group by
    voteable_id

请注意,这是ANSI SQL,因此可以跨SQLite,MySQL,Postgres,Oracle,SQL Server,DB2等工作。

要获得前N个帖子,您只需附加上述查询:

order by
    vote_score desc
limit 10
Postgres和SQLite使用

limit(在MySQL中略有不同),而不是在Oracle或SQL Server中,作为FYI。

所以,要获得与此相关的帖子信息:

select
    p.title,
    p.author,
    p.createdate,
    sum(case
            when v.vote then 1
            else -1
        end) as vote_score
from
    posts p
    inner join votes v on
        p.post_id = v.voteable_id
group by
    p.title,
    p.author,
    p.createdate
order by
    vote_score desc
limit 10

答案 1 :(得分:2)

SQLite和PostgreSQL(以及其他符合ANSI标准的SQL实现)通用的条件函数是CASE - 参见例如PostgreSQL为here,SQLite为here。所以“计数+1 /​​ -1票数”的内部部分必须是

SUM(CASE WHEN vote THEN 1 ELSE -1 END)

并且您也不可避免地需要GROUP BY voteable_id才能使SUM正常工作。

这需要在ORDER BY进行排序(使用DESC);不确定你是否也想在结果中使用它,但我会假设你这样做,在这种情况下它也需要在SELECT中(你可以在ORDER BY中引用它的别名) 。最后,LIMIT n适用于两个引擎。

所以,把它们放在一起:

  SELECT voteable_id,
         SUM(CASE WHEN vote THEN 1 ELSE -1 END) AS vote_score
  FROM votes
  GROUP BY voteable_id
  ORDER BY vote_score DESC
  LIMIT 10

应满足您的所有要求。

答案 2 :(得分:0)

如果您正在使用VoteFu(它看起来像你),那么我建议您转换为使用Integers来存储投票值而不是:boolean。

将VoteFu中的投票存储为布尔值的唯一原因是我觉得我需要保持与Acts_As_Voteable的向后兼容性。我现在已经决定我过分重视这个问题了。

我将发布一个新版本的VoteFu插件,为你处理转换,但在那之前,我认为自己改变这一点是明智之举。方法如下:

class VoteFuIntegerMigration < ActiveRecord::Migration  
  def self.up  
    add_column :votes, :vote_int, :integer
    Vote.find(:all).each do |vote|
       vote.vote_int = vote.vote? ? 1 : -1
       vote.save!
    end
    remove_column :vote, :vote
    rename_column :vote, :vote_int, :vote
  end
end

然后:

module Juixe 
  module Acts   
    module Voteable   
      module InstanceMethods  
        def votes_for  
          Vote.sum(:vote, :conditions => [  
            "voteable_id = ? AND voteable_type = ? AND vote > 0",  
            id, self.class.name])  
        end  

        def votes_against  
          Vote.sum(:vote, :conditions => [  
            "voteable_id = ? AND voteable_type = ? AND vote < 0",  
            id, self.class.name])  
        end  

        def votes_total  
          Vote.sum(:vote, :conditions => [  
            "voteable_id = ? AND voteable_type = ?",  
            id, self.class.name])  
        end  
      end
    end
  end
end

将模块更改作为monkeypatch引入可能是个好主意。新的VoteFu将集成此代码(也有测试。)