Reputation: 3584
I'm putting together a Rails app, and I've run into a bit of an SQL snag. While it's solvable in Sqlite3, when I move it to my Heroku server running Postgres, it chokes and dies. The situation is as follows:
SPORTS
-----------------------------
| id | title |
-----------------------------
| 1 | Baseball |
-----------------------------
SPORTS_VOTE
------------------------------------
| id | sport_id | vote |
------------------------------------
| 1 | 1 | 1 |
------------------------------------
The vote column could be either 1 or -1. I'd like to get a list of the sports ordered by the sum total of the votes. The query I used in Sqlite3 was something like this:
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);
In any respectable DB, this query whines about aggregate functions, etc. What is the best way to do this? Am I better off keeping a 'vote' field in the Sports table, and updating this when votes are cast? The Sports_Vote table also records the ID of the user who submitted the vote, so it's needed.
Secondly, I'd like to ideally combine two similar tables, so that I get a nice list of say, Sports AND Cookies in descending order of votes(Hockey ranked #1, Maple Cookies #2, Football #3, etc). Any advice?
Upvotes: 0
Views: 2780
Reputation: 18349
The rule of thumb to prevent errors when using group by
is:
group by
clause.Therefore, your query should be:
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);
OR
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);
To combine with a second table and rank you can just use union
(the order by
will apply to the combined result):
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;
About your question on whether you should keep the vote count in the sports table and update when each vote is cast, it depends. Doing so will give you a better performance when generating reports like the above ones (since the group by
and the inner join
won't be needed anymore), but it will reduce the performance of casting votes (since now you have to insert AND update a second table). So it depends on your priorities.
Upvotes: 4