简体   繁体   中英

Get the max value in a specific group of rows

I have these two tables:

popular_song

song_name  | rate | country_id
------------------------------
Tic Tac    | 10   | 1
Titanic    | 2    | 1
Love Boat  | 8    | 2
Battery    | 9    | 2

country

conutry_id | country
--------------------------
1          | United States
2          | Germany

What I'd like to achieve is to get the most poular song in each country, eg:

song_name | rate | country
--------------------------
Tic Tac   | 10   | United States
Battery   | 9    | Germany

I've tried this query:

SELECT MAX(rate), song_name, country
FROM popular_song ps JOIN country cnt
ON ps.country_id = cnt.country_id
GROUP BY country

But this doesn't work. I've tried looking at questions like "Order by before group by" but didn't find an answer.

Which mysql query could achieve this result?

You can use another self join to popular songs table with the max rating

SELECT ps.*,cnt.country
FROM popular_song ps
JOIN (SELECT MAX(rate) rate, country_id FROM popular_song GROUP BY country_id) t1
ON(ps.country_id = t1.country_id and ps.rate= t1.rate)
 JOIN country cnt
ON ps.country_id = cnt.conutry_id

See Demo

There is a trick that you can use with substring_index() and group_concat() :

SELECT MAX(rate),
      substring_index(group_concat(song_name order by rate desc separator '|'), '|', 1) as song,
      country
FROM popular_song ps JOIN
     country cnt
     ON ps.country_id = cnt.country_id
GROUP BY country;

EDIT:

If you have big tables and lots of songs per country, I would suggest the not exists approach:

select rate, song country
from popular_song ps join
     country cnt
     on ps.country_id = cnt.country_id
where not exists (select 1
                  from popular_song ps2
                  where ps2.country_id = ps.country_id and ps2.rate > ps.rate
                 );

Along with an index on popular_song(country_id, rate) . I recommended the group_concat() approach because the OP already had a query with a group by , so the trick is the easiest to plug into such a query.

Here is another way I'v learned from @Gordon Linoff. Here is that question you could learn too.

SELECT  ps.*,cnt.country 
FROM 
  (SELECT popular_song.*,
         @rownum:= if (@c = country_id ,@rownum+1,if(@c := country_id, 1, 1) )as row_number
         FROM popular_song ,  
              (SELECT @c := '', @rownum:=0) r
         order by country_id, rate desc) as ps
LEFT JOIN country cnt
ON ps.country_id = cnt.conutry_id
WHERE ps.row_number = 1

This is the way of implementing row_number()(Partition by ...) window function in MySql.

You can do this with EXISTS like this:

SELECT rate, song_name, cnt.country_id
FROM popular_song ps JOIN country cnt
ON ps.country_id = cnt.country_id
WHERE NOT EXISTS 
  (SELECT * FROM popular_song 
   WHERE ps.country_id = country_id AND rate > ps.rate)

It is not specified in the question whether two songs can be returned per country if their rating is the same. Above query will return several records per country if ratings are not unique at country level.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM