2012-03-15 2 views
0

Я собираю Rails-приложение, и я столкнулся с некоторой проблемой SQL. Хотя он разрешен в Sqlite3, когда я перемещаю его на сервер Heroku, работающий с Postgres, он задыхается и умирает. Ситуация такова:SQL Group By и Order By с помощью агрегатных функций

SPORTS 
----------------------------- 
| id |  title  | 
----------------------------- 
| 1  | Baseball  | 
----------------------------- 

SPORTS_VOTE 
------------------------------------ 
| id | sport_id | vote | 
------------------------------------ 
| 1 |  1  |  1  | 
------------------------------------ 

Колонка голосования может быть 1 или -1. Я хотел бы получить список видов спорта, заказанных по общей сумме голосов. Запрос я использовал в sqlite3 было что-то вроде этого:

SELECT s.title, sum(sv.vote) 
    FROM sports s 
    INNER JOIN sports_vote sv ON s.id = sv.sport_id 
GROUP BY s.id 
ORDER BY sum(sv.vote); 

В любом солидном БД, этот запрос скулит о агрегатных функций и т.д. Что это лучший способ сделать это? Лучше ли я держать поле голосования в таблице Sports и обновлять его, когда голоса подаются? В таблице Sports_Vote также записывается идентификатор пользователя, который подал голос, поэтому он необходим.

Во-вторых, я хотел бы идеально объединить две подобные таблицы, так что я получаю хороший список, скажем, Sports AND Cookies в порядке убывания голосов (Хоккей занимает 1 место, Клен Печенье № 2, Футбол № 3, и т.д). Любой совет?

ответ

4

Правило для предотвращения ошибок при использовании group by является:

  • Все столбцы не агрегированные должны быть включены в п group by.

Таким образом, ваш запрос должен быть:

SELECT s.id, s.title, sum(sv.vote) 
    FROM sports s 
    INNER JOIN sports_vote sv ON s.id = sv.sport_id 
GROUP BY s.id, s.title 
ORDER BY sum(sv.vote); 

ИЛИ

SELECT s.title, sum(sv.vote) 
    FROM sports s 
    INNER JOIN sports_vote sv ON s.id = sv.sport_id 
GROUP BY s.title 
ORDER BY sum(sv.vote); 

Чтобы объединить со вторым столом и ранжировать вы можете просто использовать union (order by будет применяться к комбинированному результат):

SELECT s.title, sum(sv.vote) vote_count 
    FROM sports s 
    INNER JOIN sports_vote sv ON s.id = sv.sport_id 
GROUP BY s.title 
UNION 
SELECT s.title, sum(sv.vote) vote_count 
    FROM cookies s 
    INNER JOIN cookies_vote sv ON s.id = sv.sport_id 
GROUP BY s.title 
ORDER BY vote_count DESC; 

О вашем вопросе о том, следует ли держать подсчет голосов в спортивном столе и обновлять, когда каждый голос подан, это зависит. Это даст вам лучшую производительность при создании отчетов, подобных приведенным выше (так как group by и inner join больше не понадобятся), но это уменьшит производительность кастования голосов (так как теперь вы должны вставить И обновить вторую Таблица). Так что это зависит от ваших приоритетов.

+1

Большое спасибо, @Diego. Кажется, это трюк. Одно быстрое наблюдение: какой самый элегантный способ сделать это в контроллере Rails? – opticon