2009-09-13 81 views
1

votes表看起來像這樣:SQL查詢來獲取「投得分」

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是班裏的選民,而voter_id是選民的ID。

我需要的是一個查詢,以獲得最高ñposts「中投得分」,其中「投得分」被定義爲(的選票後具有數)降序排列 - (中該職位的反對票數)。如果

獎勵積分您的解決方案不要求我訴諸find_by_sql()

更多的積分(我在Rails的工作),如果您的解決方案工作在SQLite和PostgreSQL的(以同樣的方式,雖然它更重要的是,它在PostgreSQL中工作)。

+0

什麼數據庫? 。 – cletus 2009-09-13 01:31:12

+3

如果您將此存儲爲1/-1而非true/false,那麼您也可以使您的生活變得更加輕鬆。 – cletus 2009-09-13 01:32:06

+0

錯誤,是不是1和0? – Alex 2009-09-15 18:34:05

回答

3

一般地,你可以通過使用case語句和sum做到這一點:

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的,甲骨文,SQL服務器,DB2工作,等等,等等

爲了讓您的前N個帖子,你會簡單地附加在上面的查詢:

order by 
    vote_score desc 
limit 10 

limit所使用的Postgres的和S QLite(在MySQL中有些不同),而不是在Oracle或SQL Server中,作爲參考。

因此,要獲得與此相關的職位信息:

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 
+0

現在又添加了前N個要求? – 2009-09-13 02:22:31

+0

酷 - 然後,我怎麼會在混合中添加一個'join'來獲得與返回的'voteable_id's相關的'posts'? – 2009-09-13 02:41:32

+0

謝謝,但是當我嘗試第二個查詢時,我得到'SQL錯誤:接近「sum」:語法錯誤' – 2009-09-13 03:58:12

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 

應滿足您的所有需求。

+0

酷 - 然後我怎麼會在混合中添加一個'join'來獲得與返回的'voteable_id'相關的'posts'? – 2009-09-13 02:40:53

+0

FROM和GROUP BY之間添加'JOIN帖子USING(voteable_id)'(如果這是兩個表的字段名,否則使用'ON'條件),並在SELECT中從'posts.'中選擇所需的字段;還要確保根據需要消除「votes.voteable_id」等歧義(當涉及> 1表時,建議對字段進行明確的限定! - )。 – 2009-09-13 02:43:23

0

如果你使用VoteFu(它看起來像你),那麼我建議你轉換爲使用整數來存儲投票值而不是: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 

這可能引進的模塊修改爲猴補丁是個好主意。新的VoteFu將集成此代碼(也包含測試)。

+0

Pete在下一個版本上的進展如何? (我仍然愛你的寶石!) – 2010-01-15 00:44:34

+0

我在編碼我的下一個版本時一直非常糟糕!其他一些項目篡奪了我。我目前正在尋找其他提交者來幫忙。如果你想從你的解決方案中提交補丁來完成整數轉換,我很樂意1)完全稱讚你,b)讓你成爲項目的提交者。讓我知道! – Pete 2010-01-25 02:17:08