rank()可找出各部门薪资最高员工,但会为并列薪资分配相同排名并跳过后续位次;需用子查询或cte包裹窗口函数才能where过滤,且必须指定partition by dept和order by salary desc,并注意null处理。

用 RANK() 找出每个部门薪资最高员工,但要注意并列处理
RANK() 会为相同薪资分配相同排名,后续跳过位次(比如两个第一,下一个就是第三),这会导致“最高”可能对应多个员工,也容易漏掉并列第一的情况。如果你只要严格意义上的“最高”,且允许并列,RANK() 是合适的;但如果业务要求“只取一个”,就得换 ROW_NUMBER() 或加额外去重逻辑。
必须用子查询或 CTE 包裹窗口函数才能 WHERE 过滤排名
SQL 中不能直接在 WHERE 子句里写 RANK() OVER (...) —— 窗口函数在 WHERE 之后执行。常见错误是写成 WHERE RANK() OVER (...) = 1,会报错 Window function is not allowed in WHERE clause。
正确做法是先计算排名,再过滤:
SELECT dept, name, salary
FROM (
SELECT dept, name, salary,
RANK() OVER (PARTITION BY dept ORDER BY salary DESC) AS rk
FROM employees
) t
WHERE rk = 1;
PARTITION BY dept 和 ORDER BY salary DESC 缺一不可
PARTITION BY dept 决定“按部门分组计算”,没它就变成全表排第几;ORDER BY salary DESC 决定“谁排第一”,漏掉 DESC 会把最低薪当第一。
- 如果薪资字段含
NULL,默认排在最前(DESC下),可能意外顶掉真实高薪员工 —— 建议加NULLS LAST(PostgreSQL/Oracle 支持),或先用WHERE salary IS NOT NULL - MySQL 8.0+、PostgreSQL、SQL Server 2005+ 支持;SQLite 3.25+ 支持;旧版 MySQL(
姓名重复或部门为空时的隐性风险
如果两个员工同部门、同最高薪,RANK() 会给两人都是 1,结果集就会有两条——这未必是 bug,而是符合预期。但若业务上要求“每个部门只返回一条”,得再加一层逻辑:
- 用
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC, name ASC)强制唯一排序依据 - 或在外层用
GROUP BY dept配合MAX(name)(但名字最大未必对应薪资最高,慎用) - 部门字段为
NULL会被归为同一组 —— 若存在未分配部门的员工,他们之间也会比出“最高薪”,常被忽略
真正难的不是写对语法,而是想清楚“最高”在你业务里到底允不允许并列、空部门怎么归类、NULL 薪资算不算有效数据。










