row_number() 必须配合 partition by department 和 order by salary desc 才能实现每个部门内按薪资降序排名;缺partition by则全局排序,错用order by字段或升序会导致逻辑错误;相同薪资仍强制不重复编号;where不能直接过滤排名列rn。

ROW_NUMBER() 必须配合 PARTITION BY 和 ORDER BY 才能分组排名
单独写 ROW_NUMBER() 不加 PARTITION BY 会把全表当一个组排序,得不到“每个部门”的结果。必须明确按部门分组、再在组内按薪资降序排——否则排名全是 1 到 N 的全局序列。
常见错误是漏掉 PARTITION BY department,或者把 ORDER BY salary 写成 ORDER BY id 导致排名逻辑错乱。
-
PARTITION BY department是分组前提,缺了就不是“每个部门” -
ORDER BY salary DESC决定谁排第一;升序会把最低薪排成第 1 名 - 如果薪资相同,
ROW_NUMBER()仍强制分配不同名次(比如 1,2,3),不并列
WHERE 条件不能直接写在原始 SELECT 后面
ROW_NUMBER() 是窗口函数,生成的排名列属于查询结果的一部分,不能在同一个 SELECT 级别用 WHERE rn 过滤——SQL 执行顺序中,<code>WHERE 在窗口函数计算前就执行了,此时 rn 还不存在。
必须用子查询或 CTE 把带排名的结果先算出来,再外层过滤。
- 错误写法:
SELECT *, ROW_NUMBER() OVER (...) AS rn FROM emp WHERE rn → 报错 <code>column "rn" does not exist - 正确做法:用
WITH ranked AS (...)或SELECT * FROM (SELECT ..., ROW_NUMBER() OVER (...) AS rn FROM emp) t WHERE t.rn - CTE 更易读,子查询嵌套过深时容易漏括号或别名
注意 NULL 薪资和重复值对排名的影响
如果 salary 字段有 NULL,默认排序时 NULLS LAST(多数数据库如 PostgreSQL、SQL Server),但 MySQL 8.0 默认 NULLS FIRST。这会导致 NULL 员工被排进“前三”,实际可能不符合业务预期。
另外,ROW_NUMBER() 对相同薪资不并列,而 RANK() 或 DENSE_RANK() 会并列。选哪个取决于需求:“薪资并列第 2 名,下一个是否算第 3 名”。
- 要严格取最多 3 行/部门 → 用
ROW_NUMBER() - 要允许并列且总数可能超 3 → 改用
RANK()(如两个第 1 名,则下一个为第 3 名) - 确保 NULL 不参与排名:加条件
WHERE salary IS NOT NULL在子查询里
完整可运行示例(PostgreSQL / MySQL 8.0+ / SQL Server)
WITH ranked AS (
SELECT
name,
department,
salary,
ROW_NUMBER() OVER (
PARTITION BY department
ORDER BY salary DESC NULLS LAST
) AS rn
FROM employees
WHERE salary IS NOT NULL
)
SELECT name, department, salary
FROM ranked
WHERE rn <p>关键点都在里面了:分组、降序、处理 NULL、两层结构。如果数据库不支持 <code>NULLS LAST</code>(比如旧版 MySQL),就改用 <code>ORDER BY salary DESC, id ASC</code> 把 NULL 排后面——因为 NULL 比任何数字都小,加个非空字段二次排序能间接控制位置。</p><p>真正上线前,记得在 <code>department</code> 和 <code>salary</code> 上建联合索引,否则大表开窗性能会明显下降。</p>










