在员工信息管理的数据库场景中,经常需要统计每个部门薪资最高的员工姓名,传统的GROUP BY分组方式只能得到每个部门的最高薪资,无法直接关联对应的员工姓名,而使用窗口函数RANK可以轻松实现这个需求,同时保留完整的员工信息。

基础表结构说明
首先假设我们有员工表employee,表结构如下:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | INT | 员工ID,主键 |
| name | VARCHAR(50) | 员工姓名 |
| dept | VARCHAR(50) | 所属部门 |
| salary | DECIMAL(10,2) | 员工薪资 |
使用RANK函数实现统计的步骤
第一步:生成部门内薪资排名
首先使用RANK窗口函数,按部门分组,组内按薪资降序排序,为每个员工生成排名,薪资最高的员工排名为1,如果有相同薪资的员工,会获得相同的排名,下一个不同薪资的排名会跳过占用的位数。
-- 生成每个部门员工的薪资排名
SELECT
id,
name,
dept,
salary,
-- 按部门分区,分区内按薪资降序排序,生成RANK排名
RANK() OVER (PARTITION BY dept ORDER BY salary DESC) AS salary_rank
FROM employee
第二步:过滤排名为1的记录
将第一步的查询结果作为子查询,过滤出salary_rank等于1的记录,就是每个部门薪资最高的员工信息,如果存在同部门同薪资的多名员工,都会被筛选出来。
-- 最终查询:统计每个部门薪资最高的员工姓名
SELECT
dept,
name,
salary
FROM (
-- 子查询:生成部门内薪资排名
SELECT
id,
name,
dept,
salary,
RANK() OVER (PARTITION BY dept ORDER BY salary DESC) AS salary_rank
FROM employee
) AS ranked_employee
WHERE salary_rank = 1
ORDER BY dept
代码逻辑解析
上述SQL的核心逻辑分为两部分:
- PARTITION BY dept:表示按部门字段分组,窗口函数的计算范围限定在每个部门内部,不同部门之间的排名互不影响。
- ORDER BY salary DESC:表示在每个部门的分组内,按薪资从高到低排序,薪资越高的员工排名数值越小。
- 外层查询的
WHERE salary_rank = 1就是过滤出每个部门排名第一的员工,也就是薪资最高的员工。
RANK与DENSE_RANK的区别
如果不需要跳过排名,比如两个员工同薪资排第1,下一个不同薪资的员工排第2,可以使用DENSE_RANK函数,只需要把上述SQL中的RANK换成DENSE_RANK即可:
-- 使用DENSE_RANK的查询示例
SELECT
dept,
name,
salary
FROM (
SELECT
id,
name,
dept,
salary,
-- 使用DENSE_RANK生成不跳过的排名
DENSE_RANK() OVER (PARTITION BY dept ORDER BY salary DESC) AS salary_rank
FROM employee
) AS ranked_employee
WHERE salary_rank = 1
ORDER BY dept
两种函数的选择取决于业务需求,如果同薪资员工需要占用多个排名位就用RANK,不需要跳过排名就用DENSE_RANK。
注意事项
- 窗口函数RANK是SQL标准函数,主流数据库如MySQL8.0+、PostgreSQL、SQL Server、Oracle都支持,低版本MySQL需要先升级版本才能使用。
- 如果部门内存在多个员工薪资相同且都是最高,使用RANK或DENSE_RANK都会返回所有符合条件的员工,符合实际业务需求。
- 排序时如果需要处理薪资为NULL的情况,可以添加
NULLS LAST或者NULLS FIRST调整NULL值的排序位置,比如ORDER BY salary DESC NULLS LAST表示薪资为NULL的员工排在最后。