2019年6月12日更新:MySQL 8已经支持row_number、rank、dense_rank、over函数,完美实现分组排序功能。




mysql怎么分组查最大日期的那一条数据_MySQL


# 使用row_number排序,注意排名没有并列
select 学号,课程号,成绩,
row_number()
over(PARTITION by 课程号 ORDER BY 成绩 desc) as 排名
from score ORDER BY 课程号,排名;


mysql怎么分组查最大日期的那一条数据_子查询_02


# 使用rank排序,注意并列排名没有连续
select 学号,课程号,成绩,
rank()
over(PARTITION by 课程号 ORDER BY 成绩 desc) as 排名
from score ORDER BY 课程号,排名;


mysql怎么分组查最大日期的那一条数据_解决方法_03


# 使用dense_rank排序,注意并列排名是连续的
select 学号,课程号,成绩,
dense_rank()
over(PARTITION by 课程号 ORDER BY 成绩 desc) as 排名
from score ORDER BY 课程号,排名;


mysql怎么分组查最大日期的那一条数据_分组 查出id最大的_04


主要理解over()开窗函数的用法,感觉它跟group by有点像,不过group by后每个组就只有一行,但是开窗函数相当于分割成多个表,然后对每个表汇总操作,且行数不变。


mysql怎么分组查最大日期的那一条数据_解决方法_05



今天遇到分组top N查询的问题,对于使用关联子查询的解决方法没有理解。暂且记录下来。


select * from score as a
where (select count(*) from score as b where a.课程号=b.课程号 and a.成绩<b.成绩)<2
order by a.课程号,a.成绩 desc;


mysql怎么分组查最大日期的那一条数据_子查询_06

各科成绩前两名

select * from score as a
where (select count(*) from score as b where a.课程号=b.课程号 and a.成绩<b.成绩)<2
order by a.课程号,a.成绩 desc;


mysql怎么分组查最大日期的那一条数据_子查询_07

各科成绩后两名

另外还有比较原始的方法是,先查出课程号(即有哪些分组),然后对每个分组的成绩排序,使用limit取topN,最后使用union all进行合并。


select 课程号 from score group by 课程号;


mysql怎么分组查最大日期的那一条数据_分组 查出id最大的_08

查看分组(课程号)

select 学号,课程号,成绩 from score
where 课程号='0001'
order by 成绩 desc
limit 2


mysql怎么分组查最大日期的那一条数据_MySQL_09

查看这一组的前两名

因此,可以将三门课程的top 2合并起来:


mysql怎么分组查最大日期的那一条数据_分组 查出id最大的_10

各科成绩的前两名

注意,第二种方法实际上是取前n条记录,对于数值相同的多条记录会漏掉。因此还是建议使用第一种方法。

第一种方法没有理解,待续……

——————————————————————————————————————

2019年5月25日,下午想明白了使用关联子查询解决分组TopN的问题。


mysql怎么分组查最大日期的那一条数据_解决方法_11

示例表

-- 求最大Top2
select * from score1 as a
where(
SELECT count(*) from score1 as b where a.课程号=b.课程号 and a.成绩 < b.成绩
)<2;


主要难以理解的是:(select count(*) from score as b where a.课程号=b.课程号 and a.成绩<b.成绩)<2。实际上这一句可以分解为两句:


(select count(*) from score as b where a.课程号=b.课程号 and a.成绩<b.成绩)=1 or
(select count(*) from score as b where a.课程号=b.课程号 and a.成绩<b.成绩)=0


抑或是:


(select count(b.成绩) from score as b where a.课程号=b.课程号 and a.成绩<b.成绩)=1 or
(select count(b.成绩) from score as b where a.课程号=b.课程号 and a.成绩<b.成绩)=0


然后重点理解这个条件:a.成绩<b.成绩。假设a.成绩的值是60/70/80/90,当然b.成绩的值也是60/70/80/90,我们可以看做a.成绩的每一行,依次与b.成绩去配对:

1)60(a.成绩)与60/70/80/90(b.成绩)配对,在a.成绩<b.成绩这一条件下,count(b.成绩)=3,即b.成绩取70/80/90才满足条件;

2)70(a.成绩)与60/70/80/90(b.成绩)配对,在a.成绩<b.成绩这一条件下,count(b.成绩)=2,即b.成绩取80/90才满足条件;;

3)80(a.成绩)与60/70/80/90(b.成绩)配对,在a.成绩<b.成绩这一条件下,count(b.成绩)=1,即b.成绩取90才满足条件;;

4)90(a.成绩)与60/70/80/90(b.成绩)配对,在a.成绩<b.成绩这一条件下,count(b.成绩)=0,即b.成绩中任意一个值都不满足条件。

因此,这个条件:select count(*) from score as b where a.课程号=b.课程号 and a.成绩<b.成绩)<2 实际上得到了a.成绩=80/90,也就是最大Top2。

同理,这个条件:select count(*) from score as b where a.课程号=b.课程号 and a.成绩>b.成绩)<2 实际上得到了a.成绩=60/70,也就是最小Top2。

____________________________________________________________________________________________

扩展:


/*有些國家的人口是同洲份的所有其他國的3倍或以上。列出 國家名字name 和 洲份 continent。*/
select a.name,a.continent
from world as a
where
-- a.表的第一人数除以3,与同组的相同的b表人数比较,大于或等于
(
select count(*) from world as b 
where a.continent=b.continent and a.population/3>=b.population
)=
-- 得到的行数是:同一分组的行数减去一
(select count(*) from world as c where a.continent=c.continent)-1


mysql怎么分组查最大日期的那一条数据_MySQL_12