2009-09-13 5 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 это идентификатор вещи быть голосование, voter_type класс избирателя, и voter_id является идентификатор избирателя.

Что мне нужно, это запрос, чтобы получить верхнюю нposts в порядке убывания «баллов голосования», где «оценка голосование» определяется как (количество до голосов пост имеет) - (количество голосов вниз, которое имеет сообщение).

Бонусные баллы, если ваше решение не требует от меня обратиться к find_by_sql() (я работаю в Rails)

Больше бонусных очков, если ваше решение работает так же, как в SQLite и PostgreSQL (хотя это более важно, что он работает в PostgreSQL).

+0

Какая база данных? , – cletus

+3

Вы также значительно облегчили бы свою жизнь, если бы сохранили это как 1/-1 вместо true/false. – cletus

+0

Err, не было бы 1 и 0? – Alex

ответ

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, Oracle, SQL Server, DB2, и т.д., и т.д.

чтобы получить ваши верхние N посты, вы бы просто добавить к выше запроса:

order by 
    vote_score desc 
limit 10 

limit используется Postgres и S QLite (немного по-другому в 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 
+0

Теперь добавьте требование сверху N? –

+0

Cool - как бы я добавил «join» в микс, чтобы получить 'posts', связанные с возвращенным' voteable_id'? –

+0

Спасибо, но когда я попробую этот второй запрос, я получаю 'SQL-ошибку: near" sum ": синтаксическая ошибка –

2

Условная функция, общая для SQLite и PostgreSQL (а также других ANSI-совместимых реализаций SQL) является CASE - смотри, например, here для PostgreSQL и here для SQLite. Таким образом, внутренняя часть для «подсчитывать +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

Cool - как бы я добавил «join» в микс, чтобы получить 'posts', связанные с возвращенным' voteable_id'? –

+0

между FROM и GROUP BY, добавьте 'JOIN posts USING (voteable_id)' (если это имя поля в обеих таблицах, в противном случае используется условие 'ON'), а в SELECT выберите нужные поля из' posts.'; также обязательно устраните 'vote.voteable_id' и т. д. по мере необходимости (рекомендуется явная квалификация полей при использовании> 1 таблицы!). –

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 

Это, вероятно, хорошая идея, чтобы ввести изменения модуля в качестве monkeypatch. Новый VoteFu будет интегрирован в этот код (с испытаниями тоже.)

+0

Прогресс на следующей версии, Пит? (Я все еще люблю ваш драгоценный камень!) –

+0

Я был ужасно, ужасно скупой в кодировании моей следующей версии! Пара другого проекта узурпировала меня. В настоящее время я ищу других коммиттеров, чтобы помочь. Если вы хотите отправить патч из своего решения, в котором есть целочисленное преобразование, я был бы рад: 1) полностью заверить вас и b) сделать вас участником проекта. Лемме знаю! – Pete

Смежные вопросы