SQL 找出分组中具有极值的行
你可能也遇到过这种需求:找出每个部门入职最早的员工的信息;获取每个科目最高分的学生信息;获取用户最近一次的完整登录信息。
这些需求有两个共同点:一是需要做分组,有按部门分组、有按科目、也有按用户分组;二是在分组里面找到存在极值的行,是整行数据,而不只是极值。
就拿 emp
举例,要从 emp
表中获取每个部门薪资最高的员工的信息。emp
表的数据如下:
最终的查询结果如下图。
要实现这个查询功能,有多少种实现方法呢?
窗口函数
如果你在用 MySQL 5.8+,窗口函数可能是你最先想到的办法,因为它足够简洁、简单。
先按部门分组,再对组内按照薪资降序排序,取排序序号为 1 的行即为部门最高薪资的员工的信息。
SELECT
empno,
ename,
job,
mgr,
hiredate,
sal,
comm,
deptno
FROM
(SELECT
*,
rank() over (
PARTITION BY deptno
ORDER BY sal DESC
) AS rk
FROM
emp) t
WHERE rk = 1
ORDER BY deptno
这里需要注意,用来排序的窗口函数使用 rank()
或者 dense_rank()
,而不能使用 row_number()
,因为有可能存在一个部门里两名或者和更多员工的薪资都是最高的,row_number()
不会给相同的排序条件分配同一个序号。
子查询
如果你的数据库还不支持窗口函数,那可以先对 emp
分组,取出每个部门中的最高薪资,再和原表做一次关联就能获取到正确的结果。
SELECT
a.*
FROM
emp a
INNER JOIN
(SELECT
deptno,
MAX(sal) AS sal
FROM
emp
GROUP BY deptno) b
ON b.deptno = a.deptno
AND b.sal = a.sal
ORDER BY deptno
上面是自然连接的写法,你也可以在WHERE
条件中使用子查询。
SELECT
a.*
FROM
emp a
WHERE a.sal =
(SELECT
MAX(sal)
FROM
emp
WHERE deptno = a.deptno)
ORDER BY deptno
外连接
外连接总能给我们带来惊喜,这次也不例外。
在此之前,你可能很难想到可以使用 Left Join
达到分组求极值的效果。现在就来揭开 Left Join
的神秘面纱。
SELECT
a.*
FROM
emp a
LEFT JOIN emp b
ON b.deptno = a.deptno
AND a.sal < b.sal
WHERE b.sal IS NULL
ORDER BY a.deptno
我们知道,在SELECT * FROM a left join b on 关联条件
语句中 ,不论在 b 表中是否有数据行可以和 a 表匹配,a 表的数据都会查询出来。不过,我们可以通过 WHERE
子句过滤 a 表返回的数据。
在关联条件 b.deptno = a.deptno AND a.sal < b.sal
中,只要 a.sal
不是分组内的最大值,总能在 b 表中找到比它大的数据。当 a.sal
是分组的内的最大值时,a.sal < b.sal
的条件不成立,关联出来的结果中 b 表的数据为 NULL
。因此,通过 WHERE b.sal IS NULL
可以找到每个分组里面 a.sal
最大的记录。
总结
前两种方法我们最可能想到,它们的写法也很容易理解,而使用外连接就需要我们多一点反向思考,需要知道使用外连接可以关联出为 NULL
的数据。
上面这几种方法都能满足前文提出的需求,至于它们之间哪个执行更快,就留给读者你去思考了。
- Linux下对lvm逻辑卷分区大小的调整(针对xfs和ext4不同文件系统)
- centos6.5虚拟机安装后,没有iptables配置文件
- 温故而知新:设计模式之Builder
- 温故而知新:设计模式之单件模式(Singleton)
- sudo命令使用的几个场景
- .NET Core系列 : 2 、project.json 这葫芦里卖的什么药
- 关闭与恢复visual studio实时调试器
- linux系统下的权限知识梳理
- c#如何启动/干掉/查找 进程
- Android中Application的应用
- 孟昭莉博士:大数据大道之行
- .NET Core系列 :3 、使用多个项目
- 完整部署CentOS7.2+OpenStack+kvm 云平台环境(2)--云硬盘等后续配置
- 完整部署CentOS7.2+OpenStack+kvm 云平台环境(3)--为虚拟机指定固定ip
- MySQL 教程
- MySQL 安装
- MySQL 管理与配置
- MySQL PHP 语法
- MySQL 连接
- MySQL 创建数据库
- MySQL 删除数据库
- MySQL 选择数据库
- MySQL 数据类型
- MySQL 创建数据表
- MySQL 删除数据表
- MySQL 插入数据
- MySQL 查询数据
- MySQL where 子句
- MySQL UPDATE 查询
- MySQL DELETE 语句
- MySQL LIKE 子句
- mysql order by
- Mysql Join的使用
- MySQL NULL 值处理
- MySQL 正则表达式
- MySQL 事务
- MySQL ALTER命令
- MySQL 索引
- MySQL 临时表
- MySQL 复制表
- 查看MySQL 元数据
- MySQL 序列 AUTO_INCREMENT
- MySQL 处理重复数据
- MySQL 及 SQL 注入
- MySQL 导出数据
- MySQL 导入数据
- MYSQL 函数大全
- MySQL Group By 实例讲解
- MySQL Max()函数实例讲解
- mysql count函数实例
- MYSQL UNION和UNION ALL实例
- MySQL IN 用法
- MySQL between and 实例讲解
- helm——部署私有库
- 查找数组中最大值的5种方法!(动图演示)
- EasyRTC-SFU之mediasoup-demo在 Windows上的编译安装
- 翻转二叉树
- optimizer.zero_grad()
- helm——工具使用举例
- Helm工具安装配置——2.14.3
- 论程序的健壮性——就看Redis
- SAP Spartacus路由参数的默认配置
- 这次用近万字的讲解带你干掉堆!
- Postgresql PL/PGSQL 程序语言系列 1 (存储过程过时了吗,与函数)
- Postgresql 从那个点看要优于 ORACLE SQL SERVER MYSQL
- VBA解析VBAProject 04——run length encoding
- TRTC Android端开发接入学习之实现语音聊天室(九)
- VBA解析VBAProject 03——解析dir流