Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get the top n results per group [duplicate]

Tags:

sql

mysql

I am using the sql to retrieve the last 20 rows from the table grouped by date. I would like to limit it so that within each post_day group only the top 10 rows votes DESC are selected.

SELECT *, DATE(timestamp) as post_day 
FROM stories 
ORDER BY post_day DESC, votes DESC
LIMIT 0, 20

This is what the table looks like:

STORYID         TIMESTAMP           VOTES
1               2015-03-10          1
2               2015-03-10          2
3               2015-03-9           5
4               2015-03-9           3
like image 327
user1829122 Avatar asked Sep 10 '15 07:09

user1829122


People also ask

How do you select top three values from each group in a table in SQL?

Lets say you need to get top 3 records for each category that means to get a top 3 subcategory with the highest amount from each category. To achieve this you can use SQL Server window/Ranking function ROW_NUMBER() Function. Following T-SQL uses SQL server ROW_NUMBER() Function.

Can we use top with GROUP BY clause?

The GROUP BY clause can help with that, but it is limited to the single top result for each group. If you want the top 5 per category, GROUP BY won't help by itself. That doesn't mean it can't be done. In fact, in today's blog, we'll learn exactly how to construct a Top N query by group.

Can I use limit with GROUP BY in SQL?

No, you can't LIMIT subqueries arbitrarily (you can do it to a limited extent in newer MySQLs, but not for 5 results per group). This is a groupwise-maximum type query, which is not trivial to do in SQL.


2 Answers

Schema

create table stories
(   storyid int auto_increment primary key,
    theDate date not null,
    votes int not null
);

insert stories(theDate,votes) values 
('2015-03-10',1),
('2015-03-10',2),
('2015-03-09',5),
('2015-03-09',3),
('2015-03-10',51),
('2015-03-10',26),
('2015-03-09',75),
('2015-03-09',2),
('2015-03-10',12),
('2015-03-10',32),
('2015-03-09',51),
('2015-03-09',63),
('2015-03-10',1),
('2015-03-10',11),
('2015-03-09',5),
('2015-03-09',21),
('2015-03-10',1),
('2015-03-10',2),
('2015-03-09',5),
('2015-03-09',3),
('2015-03-10',51),
('2015-03-10',26),
('2015-03-09',75),
('2015-03-09',2),
('2015-03-10',12),
('2015-03-10',44),
('2015-03-09',11),
('2015-03-09',7),
('2015-03-10',19),
('2015-03-10',7),
('2015-03-09',51),
('2015-03-09',79);

The Query

set @rn := 0, @thedate := '';
select theDate, votes
from 
(
   select storyid, theDate, votes,
      @rn := if(@thedate = theDate, @rn + 1, 1) as rownum,
      @thedate := theDate as not_used
  from stories
  order by theDate, votes desc
) A
where A.rownum <= 10;

The Results

+------------+-------+
| theDate    | votes |
+------------+-------+
| 2015-03-09 |    79 |
| 2015-03-09 |    75 |
| 2015-03-09 |    75 |
| 2015-03-09 |    63 |
| 2015-03-09 |    51 |
| 2015-03-09 |    51 |
| 2015-03-09 |    21 |
| 2015-03-09 |    11 |
| 2015-03-09 |     7 |
| 2015-03-09 |     5 |
| 2015-03-10 |    51 |
| 2015-03-10 |    51 |
| 2015-03-10 |    44 |
| 2015-03-10 |    32 |
| 2015-03-10 |    26 |
| 2015-03-10 |    26 |
| 2015-03-10 |    19 |
| 2015-03-10 |    12 |
| 2015-03-10 |    12 |
| 2015-03-10 |    11 |
+------------+-------+
20 rows in set, 1 warning (0.00 sec)
like image 167
Drew Avatar answered Oct 28 '22 19:10

Drew


Usually you should use ROW_NUMBER() per group to order records inside of each group and then select records with ROW_NUMBER <= 10. In MySQL there is no ROW_NUMBER() aggregate function but you can use User-Defined variables in MySQL to emulate ROW_NUMBER()

select storyId, post_day , votes
from (
   select storyId,
          DATE(timestamp) as post_day, 
          votes,
          @num := if(@grp = DATE(timestamp), @num + 1, 1) as row_number,
          @grp := DATE(timestamp) as dummy
  from stories,(select @num := 0, @grp := null) as T
  order by DATE(timestamp) DESC, votes DESC
) as x where x.row_number <= 10;

SQLFiddle demo

Also look at: How to select the first/least/max row per group in SQL

like image 1
valex Avatar answered Oct 28 '22 19:10

valex