ROW_NUMBER()必须配合OVER()使用,否则报错;其基本形式为ROW_NUMBER() OVER (PARTITION BY col1 ORDER BY col2),且ORDER BY不可省略,取每组前N名需用子查询或CTE包裹后再WHERE过滤。

ROW_NUMBER() 必须配合 OVER() 才能用
单独写 ROW_NUMBER() 会直接报错,比如 SELECT ROW_NUMBER() 或 SELECT ROW_NUMBER() FROM t 都不合法。它本质是个窗口函数,必须通过 OVER() 定义计算范围和排序逻辑。
常见错误是漏掉括号里的 ORDER BY —— 即使只想要编号不关心顺序,也得显式写个字段,否则语法不通过。如果真不需要业务意义的排序,可用主键或 ORDER BY (SELECT NULL)(部分数据库支持),但更稳妥的是选一个确定性字段,比如 id。
分组内排序靠 PARTITION BY + ORDER BY
要在每个分组里独立编号,关键不是“分组”,而是用 PARTITION BY 切割数据域。它把结果按指定列值划成若干块,ROW_NUMBER() 在每块内从 1 开始重新计数。
示例:按部门统计员工薪资排名
SELECT name, dept, salary, ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn FROM employees;
PARTITION BY dept 表示每个 dept 值为一组;ORDER BY salary DESC 决定组内编号顺序。注意:PARTITION BY 和 ORDER BY 的字段不必出现在 SELECT 列表中,但必须在源表或子查询中存在。
- 如果
PARTITION BY字段有 NULL,多数数据库会把所有 NULL 归为同一组 -
ORDER BY中若存在相同值(如两人同薪),ROW_NUMBER()仍强制赋予不同序号,不会并列 - 想实现“并列排名”,得换用
RANK()或DENSE_RANK()
WHERE 不能直接过滤 ROW_NUMBER 结果
刚写完 ROW_NUMBER() OVER(...),常想加 WHERE rn = 1 取每组第一条——这会报错,因为窗口函数在 WHERE 之后执行。必须用子查询或 CTE 包一层。
正确写法:
SELECT * FROM (
SELECT
name, dept, salary,
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn
FROM employees
) t
WHERE t.rn = 1;CTE 更清晰:
WITH ranked AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn FROM employees ) SELECT name, dept, salary FROM ranked WHERE rn = 1;
别在 WHERE 或 HAVING 里直接引用 rn,这是 SQL 执行顺序决定的硬约束,不是方言差异。
性能和兼容性要注意这些点
ROW_NUMBER() 在大数据量下可能慢,尤其 PARTITION BY 字段基数高(比如按用户 ID 分组,上千万组),或者 ORDER BY 字段没索引。MySQL 8.0+、PostgreSQL、SQL Server、Oracle 都支持,但 SQLite 直到 3.25.0 才加入窗口函数,旧版本会报错。
- PostgreSQL 中
PARTITION BY支持表达式,如PARTITION BY dept || '_v2';MySQL 要求必须是列名或简单表达式 - 如果排序字段有大量重复值,且你依赖
rn = 1取唯一记录,建议在ORDER BY末尾加个唯一字段(如ORDER BY salary DESC, id ASC)避免结果不稳定 - 某些场景下,用
GROUP BY+ 聚合函数比窗口函数更高效,比如只取每组最大值而不关心具体哪一行
真正麻烦的不是语法,是搞清你要的是“每组第一条”还是“每组按某规则排好序后的第 N 条”——前者常需稳定排序键,后者可能涉及偏移或动态 N,得根据业务再套一层逻辑。

















