3

I am making a music player where we have stations. I have a table called histories. It has data on the songs a user likes, dislikes or skipped. We store all the times that a person has liked a song or disliked it. We want to get a current snapshot of all the songs the user has either liked (event_type=1) or disliked (event_type=2) in a given station.

The table has the following rows:

  • id (PK int autoincrement)
  • station_id (FK int)
  • song_id (FK int)
  • event_type (int, either 1, 2, or 3)

Here is my query:

SELECT song_id, event_type, id 
FROM histories 
WHERE id IN (SELECT MAX(id) AS id 
             FROM histories 
             WHERE station_id = 187 
               AND (event_type=1 OR event_type=2) 
             GROUP BY station_id, song_id)  
ORDER BY id;

Is there a way to make this query run without the inner select? I am pretty sure this will run a lot faster without it

Mahmoud Gamal
  • 78,257
  • 17
  • 139
  • 164
jamesatha
  • 7,280
  • 14
  • 37
  • 54
  • If this is MySQL, show us what putting `EXPLAIN` in front of the rest of your query will produce. Any query that's not pulling from an indexed or key column will slow it down. – Makoto Apr 24 '13 at 06:37
  • why are you pretty sure? have you examined the query plan and found out what is wrong with it, and is it really about the inner select or something else? – eis Apr 24 '13 at 06:37
  • a simple query seems to be enigh as described `SELECT song_id, event_type, id FROM histories WHERE station_id = 187 AND (event_type=1 OR event_type=2) ORDER BY id;` – Raab Apr 24 '13 at 06:42
  • If this is in SQL 2012 or Oracle 11g you can use analytical functions to speed it up. But I see you already have an answer. – Nick.Mc Apr 24 '13 at 08:17

3 Answers3

5

You can use JOIN instead. Something like this:

SELECT h1.song_id, h1.event_type, h1.id 
FROM histories AS h1
INNER JOIN
(
   SELECT station_id, song_id, MAX(id) AS MaxId
   FROM histories 
   WHERE station_id = 187 
     AND event_type IN (1, 2) 
   GROUP BY station_id, song_id
)  AS h2  ON h1.station_id = h2.station_id 
         AND h1.song_id    = h2.song_id
         AND h1.id         = h2.maxid
ORDER BY h1.id;
Mahmoud Gamal
  • 78,257
  • 17
  • 139
  • 164
3

@Mahmoud Gamal answer is correct, you probably can get rid of the some conditions that is not needed.

SELECT h1.song_id, h1.event_type, h1.id 
FROM histories AS h1
INNER JOIN
(
   SELECT MAX(id) AS MaxId
   FROM histories 
   WHERE station_id = 187 
     AND event_type IN (1, 2) 
   GROUP BY song_id
)  AS h2  ON h1.id = h2.maxid
ORDER BY h1.id;
mkhatib
  • 5,089
  • 2
  • 26
  • 35
0

Based on your description, this is the answer:

SELECT DISTINCT song_id, event_type, id 
FROM histories 
WHERE station_id = 187 
AND (event_type=1 OR event_type=2) 
ORDER BY id

But you must be doing the MAX for some reason - why?

Nick.Mc
  • 18,304
  • 6
  • 61
  • 91
  • We need the MAX since i want the current snapshot. If a song is liked and later disliked in the station, the latest one (with the highest id value) needs to be the one we return – jamesatha Apr 24 '13 at 06:40