RANK()窗口函数可按部门分组并依薪资降序排名,相同薪资名次相同且后续跳号;须用PARTITION BY department和ORDER BY salary DESC,过滤需用CTE或子查询。

用 RANK() 窗口函数分部门排薪资名次
RANK() 是最直接满足“按部门分组、按薪资降序排名”的窗口函数。它会为相同薪资赋予相同名次,后续名次跳过(比如两个第1名后是第3名)。注意它必须配合 OVER 子句使用,且 ORDER BY 必须写在 OVER 里,不能写在外部 ORDER BY 中——否则排名逻辑会错乱。
关键点:
-
RANK()的排序方向直接影响名次:要“最高薪排第一”,必须用ORDER BY salary DESC - 分区必须明确写
PARTITION BY department,漏掉就变成全公司统一排名 - 不能在
WHERE子句里直接过滤RANK() = 1这类计算列——SQL 执行顺序中WHERE在窗口函数之前,会报错column "rank" does not exist
过滤出每部门排名前三的记录(子查询 or CTE)
因为窗口函数结果不可在同级 WHERE 中引用,必须把带 RANK() 的查询包一层。推荐用 CTE,语义清晰、可读性好;子查询也行,但嵌套深了易出错。
示例(PostgreSQL / MySQL 8.0+ / SQL Server):
rk 会包含并列情况(例如某部门有 4 人并列第 2 名,则实际返回 5 条),这是 RANK() 的行为特性,不是 bug。
- HR 要“每个部门薪资最高的三个人”,但数据里常有同薪,这时得和业务确认:并列第 3 名要不要算?
- 用
ROW_NUMBER()时,相同薪资因无稳定排序依据(如id或name)会导致每次执行结果不一致,建议补一个确定性ORDER BY第二字段 -
RANK()在 NULL 值处理上各数据库不同(有的把 NULL 当最大,有的当最小),若salary可为空,先WHERE salary IS NOT NULL
性能与索引建议
窗口函数本身不走索引,但 PARTITION BY + ORDER BY 组合是优化重点。如果查询频繁,建议在 (department, salary) 上建联合索引(注意 salary 排序方向需匹配:DESC)。
另外,大数据量下避免在 CTE 或子查询里做多余计算——比如不需要的字段别 SELECT 出来,RANK() 只用于过滤时,不必 SELECT 到最终结果里。
真正容易被忽略的是:当部门数量极少(比如只有 3 个)、但员工数极多时,PARTITION BY department 的分组开销反而可能比全表扫描还重;这种情况下,手写三个 UNION ALL 加 LIMIT 3 反而更快——别迷信窗口函数万能。










