Skip to content
Advertisement

Complicated MySQL Select query

Here is my table layout:

Screenshot of database layout

What I would like to do:

  1. Count the number of votes for each songID in a particular room (passed in as a parameter).
  2. Find out if the userID has at least one vote for a song in a particular room. UserID is also passed in as a parameter.
  3. An extension of the two above. If there are no votes for a song, I would like to return 0 for those entries.

What I have:

select a.SongID, count(*) as voteCount, b.UserID 
  from votes a left join votes b 
    on a.songID = b.songID 
   and b.RoomID = 178 
   and b.UserID = 71 
 where a.RoomID = 178 
 group by a.SongID, b.UserID

(feel free to use those RoomID and UserID numbers in examples)

The above SQL statement does 1 and 2 but not 3. Is there any way to extend it to do 3? It would be nice to get all this done in a single query. Right now it returns all songs that have a vote, the number of votes and if the UserID in question has voted (null otherwise). Thanks in advance.

Just to clarify a bit, currently, this statement will only output songs with entries in the votes table, I’m trying to modify it to include songs that do not have entries in the votes table (that is, to include all songs in Select SongID from songs where RoomID = 178). Also keeping in mind everything is room-specific. Songs are matched to a certain room by their SongID.

Here is my song table for example:

+--------+--------+
| SongID | RoomID |
+--------+--------+
|   2835 |    178 |
|   2836 |    178 |
|   2837 |    178 |
|   2838 |    178 |
|   2839 |    178 |
|   2840 |    178 |
|   2841 |    178 |
|   2842 |    178 |
|   2843 |    178 |
|   2844 |    178 |
|   2845 |    178 |
|   2846 |    178 |
|   2847 |    178 |
|   2848 |    178 |
+--------+--------+

Here is the votes:

+--------+--------+--------+
| SongID | UserID | RoomID |
+--------+--------+--------+
|   2835 |     71 |    178 |
|   2836 |     71 |    178 |
|   2837 |     71 |    178 |
|   2838 |     71 |    178 |
|   2839 |     71 |    178 |
|   2840 |     71 |    178 |
|   2841 |     71 |    178 |
|   2842 |     71 |    178 |
+--------+--------+--------+

And finally, the result of the query I have so far:
Only the first 7 songs have votes and only those are returned. I would like to modify this query to include ALL songs that are in the room, even if they have no votes (and return the vote count as 0 or null).

+--------+-----------+--------+
| SongID | voteCount | UserID |
+--------+-----------+--------+
|   2835 |         1 |     71 |
|   2836 |         1 |     71 |
|   2837 |         1 |     71 |
|   2838 |         1 |     71 |
|   2839 |         1 |     71 |
|   2840 |         1 |     71 |
|   2841 |         1 |     71 |
+--------+-----------+--------+

Advertisement

Answer

I believe your query might look like this

SELECT s.SongID, 
       COALESCE(v.allVotes, 0) allVotes, 
       COALESCE(v.userVotes, 0) userVotes
  FROM songs s LEFT JOIN 
(
  SELECT SongId, 
         COUNT(*) allVotes,
         SUM(CASE WHEN UserID = 71 THEN 1 ELSE 0 END) userVotes
    FROM votes
   WHERE RoomID = 178 
   GROUP BY SongID
) v 
    ON s.songID = v.songID 
 WHERE s.RoomID = 178 

Here is SQLFiddle demo

User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement