ROW_NUMBER() 是窗口函数,必须配合 OVER() 使用且 OVER 内至少含 ORDER BY;不能出现在 WHERE 或 GROUP BY 中,需用子查询或 CTE 过滤结果。

ROW_NUMBER() 必须配合 OVER() 使用,且 OVER 里至少要有 ORDER BY,否则直接报错。
为什么一写就报“缺少 OVER 子句”或“窗口函数不能出现在 WHERE 中”?
因为 ROW_NUMBER() 是窗口函数,不是普通标量函数——它不参与 WHERE、GROUP BY 的执行阶段,只在 SELECT 阶段生成结果。常见错误包括:
- 直接写
WHERE ROW_NUMBER() OVER (...) = 1→ 报错:窗口函数不能出现在 WHERE 中 - 漏掉
OVER或里面没写ORDER BY→ 报错:Incorrect syntax near 'ORDER'或类似提示 - 在 GROUP BY 后试图用
ROW_NUMBER()→ 逻辑冲突,分组后行已聚合,窗口函数无意义
怎么给全表按某列排序并加行号?
最基础用法就是全局编号,比如按销量从高到低排,每行一个严格递增序号:
SELECT product_name, sales_amount, ROW_NUMBER() OVER (ORDER BY sales_amount DESC) AS rn FROM products;
注意点:
-
ORDER BY在OVER里,和最终SELECT的排序无关(可以再加外部ORDER BY,但不影响rn值) - 相同
sales_amount值也会被强行分出 1、2、3…… 没有并列 - 如果想打破并列歧义(比如两个 95 分不知谁先),可追加二级排序:
ORDER BY score DESC, id ASC
怎么按分组各自排名(比如每个部门内薪资 Top 3)?
用 PARTITION BY 划分区,每个区独立编号:
SELECT department, employee_name, salary, ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS dept_rank FROM employees;
关键行为:
- 每个
department内部从 1 开始编号,跨部门不连续 - 必须和子查询配合才能过滤(如取每个部门前 3 名):
WHERE dept_rank 要写在外层,不能写在窗口计算的同一层 - MySQL 8.0+、PostgreSQL、SQL Server、Oracle 都支持;老版本 MySQL(
真正容易被忽略的是:分区字段类型要一致,比如 PARTITION BY status,如果 status 有 NULL 值,不同数据库处理方式不同(SQL Server 把所有 NULL 归为同一组,PostgreSQL 默认也如此,但某些配置下可能拆开)。别让 NULL 悄悄改变你的分组边界。

















