SQL-запрос, чтобы получить «счет голосования» - PullRequest
1 голос
/ 13 сентября 2009

Моя 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 - это идентификатор того, за что голосуют, voter_type - это класс избирателя и voter_id - это идентификатор избирателя.

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

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

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

Ответы [ 3 ]

3 голосов
/ 13 сентября 2009

Как правило, вы можете сделать это, используя оператор 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 и SQLite (немного по-другому в 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
2 голосов
/ 13 сентября 2009

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

Если вы используете 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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...