row_number() 不能直接在 where 中使用,因其是窗口函数,执行顺序在 where 之后;需用子查询或 cte 包裹后在外层筛选。

ROW_NUMBER() 为什么不能直接在 WHERE 中用
因为 ROW_NUMBER() 是窗口函数,执行顺序在 WHERE 之后、ORDER BY 之前。你写 WHERE rn = 1 会报错 —— rn 这个别名在 WHERE 阶段还不存在。
常见错误现象:ERROR: column "rn" does not exist 或类似提示(不同数据库略有差异)。
解决思路:必须把带 ROW_NUMBER() 的查询包成子查询或 CTE,再在外层筛选。
- 子查询方式最通用,所有主流 SQL 引擎都支持
- CTE 更易读,但某些旧版 MySQL(
- 别在
GROUP BY或HAVING里试图引用窗口函数结果
用子查询实现“每个部门薪资最高者”的关联查询
典型场景:查出每个部门工资最高的员工,并连带部门名称(来自 departments 表)。
关键点在于:先用 ROW_NUMBER() 排序分组,再 JOIN 关联其他表 —— 顺序不能反。
SELECT t.*, d.dept_name
FROM (
SELECT emp_id, dept_id, salary,
ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rn
FROM employees
) t
JOIN departments d ON t.dept_id = d.dept_id
WHERE t.rn = 1;
注意:PARTITION BY dept_id 控制“每组内排序”,ORDER BY salary DESC 决定谁排第一;如果有多人同薪并列第一,ROW_NUMBER() 会强制给唯一序号(可能随机),要并列请改用 RANK() 或 DENSE_RANK()。
JOIN 前还是 JOIN 后加 ROW_NUMBER()?性能差异明显
如果关联表很大(比如 departments 有上万行),先 JOIN 再 ROW_NUMBER() 会导致窗口函数在笛卡尔积级别计算,极慢。
正确做法:只在主表(employees)上算排名,拿到结果后再 JOIN —— 数据量最小化。
- ❌ 错误:先
JOIN再ROW_NUMBER() OVER (PARTITION BY d.dept_name ...) - ✅ 正确:子查询中只处理
employees,ROW_NUMBER()计算完再JOIN departments - MySQL 8.0+ / PostgreSQL / SQL Server 都适用;SQLite 3.25+ 也支持窗口函数
遇到 NULL 或重复排序字段时 ROW_NUMBER() 怎么办
当 ORDER BY 字段含 NULL,不同数据库默认行为不一致:PostgreSQL 把 NULL 当最大值,MySQL 可能当最小值。容易导致“本该第一的人被排到末尾”。
更隐蔽的问题是:用 salary 排序但多人同薪,ROW_NUMBER() 的“随机性”会让结果不可复现(尤其跨执行批次)。
- 显式控制 NULL:加
NULLS LAST(PostgreSQL/Oracle)或用COALESCE(salary, 0)替换 - 保证稳定性:在
ORDER BY后追加主键,如ORDER BY salary DESC, emp_id ASC - 别依赖数据库默认的 NULL 排序行为,测试时用
IS NULL单独验证过再上线
真正麻烦的不是语法,而是窗口函数的执行时机和数据边界 —— 尤其在 JOIN 多张大表时,少一层嵌套可能省几秒,多一个 NULL 可能漏掉关键记录。










