之前在学习SQL时刷过一遍LeetCode上的SQL题,不过只做一遍效果并不是很好,很快也忘记了具体的解题思路。在这里将对其中的:Q176(第二高薪水) 、 Q177(第N高薪水) 、 Q178(分数排名) 、 Q184(部门工资最高的员工) 、 Q185(部门工资前三高的员工) 进行归纳总结,从而更进一步的去理解有关排名和分组筛选相关的问题。
LeetCode上的SQL答案可详见Github-LeetCode,欢迎Start,Issue
Leetcode上这五道题放在一起看,其考察的知识点可以拓展为下面三个方向:

  • 不分组筛选问题(最二值、第N个值、前N个值)
  • 分组筛选问题(最大值、第N个值、前N个值)
  • 排名问题

乍一看排名问题貌似和筛选关系不大,实则不然。基于排名的思想可以很容易实现比较复杂的筛选问题。当然相比于分组筛选,不分组筛选的实现难度还是比较低的。接下来将会逐一分析这三类问题。首先来说说排名问题。
注:本文所有的样例SQL对应的数据表如下所示

CREATE TABLE `empl` (`id` int(11) NOT NULL AUTO_INCREMENT,`salary` int(11) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;CREATE TABLE `employee` (`id` int(11) NOT NULL AUTO_INCREMENT,`salary` int(11) NOT NULL,`deparment` varchar(64) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

对应的数据内容如下

排名

MySQL没有提供可供直接使用的排名函数,所以我们必须自己来实现。常见的排名场景可以分为如下三类:

  • 非跳跃式同分同名排名
  • 跳跃式同分同名排名
  • 同分不同名排名

因为同分不同名在实际使用的时候并不多见,而且实现起来也比较容易,所以在合理就不做讨论;主要来看看其他两种应用场景。

非跳跃式同分同名排名

顾名思义排名的方式是不间断的,是连续的。Eg:对数据500, 400, 400, 300, 200, 100进行飞跳跃式排名,则结果为:1,2,2,3,4,5。以上述的empl表为例,实现SQL如下:

SELECTsalary,rank
FROM(SELECTsalary,@rank :=IF (salary = @sal, @rank, @rank + 1) AS rank,@sal := salaryFROMempl a,(SELECT @rank := 0, @sal := NULL) bORDER BYsalary DESC) c;

跳跃式同分同名排名

排名的结果是不连续的,Eg:对数据500, 400, 400, 300, 200, 100进行飞跳跃式排名,则结果为:1,2,2,4,5,6。以上述的empl表为例,实现SQL如下:

SELECTsalary,rank
FROM(SELECTsalary,@rank :=IF (salary = @sal, @rank, @row_num) AS rank,@sal := salary,@row_num :=@row_num + 1FROMempl a,(SELECT@rank := 0,@sal := NULL,@row_num := 1) bORDER BYsalary DESC) c;


相比于非跳跃式的实现,跳跃式排名需要额外增加一个变量row_num来记录当前数据对应的行数(从1开始自增长),排名的依据也必须基于该row_num,然后非是rank+1。

不分组筛选

不分组的筛选将从通用场景(获取第N个值,获取前N个值)和特殊场景(获取第二个值,获取第一和第二值)两个维度来进行讨论。其中实现通用场景的方法通常有三个:

  • 1,自身左连接法
  • 2,子查询法
  • 3,排序分页法
    从实现思路的角度考虑,方法1和方法2其实是一回事。只是基于一种思想的两种不同实现方式而已。因为SQL的left join都是可以正常改写成子查询的方式,只是在查询性能上要远好于子查询。

不分组筛选 - 获取前N个值

自身左连接法

筛选empl表中薪水最大的前三个值,实现思路:假设数据集中没有重复数据,则不难想到,最大的值不存在比其自身更大的数据项,第二大值只存在一个比自己大的数据项,第三大值有且只有两个比自己大的值;以此类推将自身和比自己大的数据表做join,则可以通过分组计数的方式获取前N个值。SQL如下

SELECTa.salary AS salary
FROM(SELECT DISTINCTsalaryFROMempl) a
LEFT JOIN (SELECT DISTINCTsalaryFROMempl
) b ON (a.salary < b.salary)
GROUP BYa.salary
HAVINGcount(*) < 3
ORDER BYsalary DESC;

子查询法

将上述left join改写成子查询,SQL如下:

SELECTsalary
FROM(SELECT DISTINCTsalaryFROMempl) a
WHERE3 > (SELECTcount(*)FROM(SELECT DISTINCTsalaryFROMempl) bWHEREa.salary < b.salary)
ORDER BYsalary DESC;

排序分页法

这是一种不同是思考方式,首先对原始数据进行倒排去重,然后根据limit + offset的组合实现筛选前N项。SQL如下:

SELECT DISTINCTsalary
FROMempl
ORDER BYsalary DESC
LIMIT 3 OFFSET 0;

不分组筛选 - 获取第N个值

不分组获取第N个值的思路和获取前N个值完全一样,只是在筛选数据的时候改变一下数据的筛选条件即可。
如果使用的是left join或子查询的方式,则可以改写Having语句为HAVING count(*) = N - 1即可。不过这里的N必须大于3,因为无法通过HAVING count(*) = 1来筛选第二大值,无法通过HAVING count(*) = 0来筛选最大值;因为left join()后的最大值的数据集为(id, salary,null, null),第二大值的数据集为(id, salary, id, max(salary)),所以对于这两种特殊场景建议使用特殊的筛选方式(详见下文)。
如果使用的是排序分页法,则可以通过LIMIT 1 OFFSET N - 1来实现获取第N个值。

不分页获取第二个值

可以通过双Max()来实现

SELECTmax(salary) AS sal
FROMempl
WHEREsalary <> (SELECT max(salary) FROM empl);

排名法

当然所有的场景均可以通过排名法来实现,具体的实现SQL在这里就不做列举,可参考前文中有关排名的SQL实现语句。

分组筛选

相比于不分组的筛选,实现分组筛选的核心思路基本不变,只是在原有的基础上增加了分组的操作。这里的分组不能通过MySQL提供的Group By来直接实现,因为Group By往往是要和聚合函数或者Having一起使用的,MySQL只有求最大、最小这类基本的函数,并没有Top-N的函数,所以还是需要基于条件的left join方法或者分组排名的方式来实现。

自身左连接法

实现筛选表employee中各部门薪水最高的前三个;只需要在上文的基础上,增加分组操作即可。同样需要保证数据集无不重复元素,SQL如下:

SELECTa.salary,a.deparment
FROM(SELECT DISTINCTsalary,deparmentFROMemployee) a
LEFT JOIN (SELECT DISTINCTsalary,deparmentFROMemployee
) b ON (a.deparment = b.deparmentAND a.salary < b.salary
)
GROUP BYa.deparment,a.salary
HAVINGcount(*) < 3
ORDER BYa.deparment,a.salary DESC;

子查询法

将上述left join进行改写,SQL如下:

SELECT*
FROM(SELECT DISTINCTdeparment,salaryFROMemployee) a
WHERE3 > (SELECTcount(*)FROM(SELECT DISTINCTdeparment,salaryFROMemployee) bWHEREa.deparment = b.deparmentAND a.salary < b.salary)
ORDER BYa.deparment,a.salary DESC;

非跳跃式同分同名分组排名法

筛选各部门薪水最高的前三个,SQL如下:

SELECT DISTINCTsalary,deparment
FROM(SELECTdeparment,salary,@rank :=IF (@dep = deparment,IF (@sal = salary,@rank :=@rank,@rank :=@rank + 1),1) AS rank,@sal := salary,@dep := deparmentFROMemployee a,(SELECT@rank := 0,@sal := NULL,@dep := NULL) bORDER BYdeparment,salary DESC) c
WHERErank <= 3;

基于本地变量实现排名的前提是要保证数据的有序性(降序)。而分组做Top-N筛选则首先需要保证各分组的有序性(降序),然后每对一个分组进行排名;在每完成一个分组的排名并开始一下分组排名时,需要将rank置为1,只有这样才能保证下一个分组排名结果的正确性;所以这里采用了嵌套IF的方式,其中外层用于判断是否更换分组,而内层用于进行非跳跃式同分同名排名。也正因为同分同名的缘故,所以最后筛选时需要对薪水进行Distinct,保证结果的唯一性。

分组筛选最大值

上面介绍了分组筛选的三种通用解决办法,这里在介绍一种求最大值这种特殊场景的特殊实现方法:首先分组计算最大值,然后连表做join;SQL如下:

SELECT DISTINCTemployee.salary,employee.deparment
FROMemployee
INNER JOIN (SELECTmax(salary) AS salary,deparmentFROMemployeeGROUP BYdeparment
) AS tmp ON employee.salary = tmp.salary
AND employee.deparment = tmp.deparment;

MySQL实现排名、分组筛选、TopN问题相关推荐

  1. MySQL 分类排名(并列、不并列),分组TOP N,ROW_NUMBER()函数

    目录 表结构 题目一:获取每个科目下前五成绩排名(允许并列) 分析: 题目二:获取每个科目下最后两名学生的成绩平均值 分析: 题目三:获取每个科目下前五成绩排名(不允许并列) 分析: ROW_NUMB ...

  2. 实战:判断mysql中当前用户的连接数-分组筛选

    #connets.sh #!/bin/sh #ocpyang@126.com #根据输入参数u或d来显示出对应的用户名或数据库名中用户的连接数. #也可以输入u 具体用户名或d 具体数据库名做进一步的 ...

  3. MYSQL —(二)筛选、聚合和分组、查询

    MYSQL -(二)筛选.聚合和分组.查询 虚拟机清屏:Ctrl+l 筛选条件 比较运算符 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e9UNlksr-158046 ...

  4. MySQL 实现排名(分组排名)

    在MYSQL的最新版本MYSQL8已经支持了排名函数RANK,DENSE_RANK和ROW_NUMBER.但是在5.*版本中还不支持这些函数,只能自己实现.实现方法主要用到了条件判断语句(CASE W ...

  5. MySQL:基础—数据分组

    MySQL:基础-数据分组 1.为什么要分组: 比如一个表中有多条订单记录,如上图,每条记录对应着一个商品,现在我要查询 每个商品被订购的单数 准备出货?也就是找到每个商品被订购的数量. 如果只找一个 ...

  6. mysql数据库如何分组查询_数据库MySQL--分组查询

    分组数据:group by 子句 分组查询语法: select 分组函数,列(要求是出现在group by的后面) from 表 (where 筛选条件)  # where 必须连接from关键字 g ...

  7. mysql 计算排名,生成排行榜

    mysql计算排名,获取行号rowno 学生成绩表数据 SELECT * FROM table_score ORDER BY score DESC; 获取某个学生成绩排名并计算该学生和上一名学生成绩差 ...

  8. java 索引排序_Java培训MySQL之排序分组优化索引的选择

    索引的选择 ①首先,清除emp上面的所有索引,只保留主键索引! drop index idx_age_deptid_name on emp; ②查询:年龄为30岁的,且员工编号小于101000的用户, ...

  9. Mysql 的 排序分组优化

    Mysql 的 排序分组优化 where 条件和 on的判断这些过滤条件,作为优先优化的部门,是要被先考虑的!其次,如果有分组和排序,那么也要考虑grouo by 和order by. 1. 无过滤不 ...

最新文章

  1. swift 学习- 14 -- 继承
  2. 使用onnx包将pth文件转换为onnx文件
  3. java web 项目打成war包部署到服务器上
  4. netflix_Netflix的计算因果推论
  5. 10个Python面试常问的问题
  6. DEDE留言板调用导航的方法
  7. C语言课后习题(51)
  8. 1031. 查验身份证(15)-PAT乙级真题
  9. javascript 方法总结(Array篇)
  10. 【斯坦福大学公开课CS224W——图机器学习】六、图神经网络1:GNN模型
  11. 网站克隆工具_全员惊艳!强推5款高质量的小众实用网站
  12. 图学习(二)K-armed Bandit based Multi-modal Network Architecture Search for Visual Question Answering
  13. 怎么将照片尺寸调整成两寸?照片调整尺寸方法分享
  14. win10蓝屏提示重新启动_关于网传0x000000F4蓝屏的临时分析解答
  15. 计算机配置ppt制作,做ppt.ps要用什么样的配置的手提电脑做
  16. @Validated规则校验和校验分组Group
  17. 黑客组织 LAPSUS$ 认领,英伟达超7万员工信息遭泄露时间线
  18. angular4子路由辅助路由
  19. 【服务器数据恢复】华为OceanStor T系列存储数据恢复案例
  20. Nutanix 连续四年荣获 Gartner 超融合基础架构魔力象限领导者称号

热门文章

  1. android listview视差滚动,android – ListView在滚动时覆盖另一个布局
  2. 吴军《数学之美》部分概念笔记(1-11章)
  3. Linux 网卡驱动的安装
  4. 微信小程序页面之间传参,发表说说
  5. 手机通过QQ发送图片到电脑端,电脑接收不到且显示图裂
  6. Win 32API速查
  7. 软件缺陷及其生命周期
  8. c语言100以内分解质因数,用C语言实现,将100以内的自然数分解质因数
  9. 北京车辆从4S店提车以后自己上牌怎么操作
  10. 知识?一文解析币圈一级二级市场 原来币圈居然这么多要知道的