ROW_NUMBER() 是窗口函数,必须配合 OVER() 使用,需指定 PARTITION BY 和 ORDER BY;单独使用会报错;取每组前N名需用子查询或CTE包裹。

ROW_NUMBER() 必须配合 OVER() 使用,否则直接报错
单独写 ROW_NUMBER() 会触发类似 Window function is missing OVER clause 的错误。它不是普通函数,而是窗口函数,必须明确指定分区(PARTITION BY)和排序(ORDER BY)逻辑。
常见误写:
SELECT name, score, ROW_NUMBER() FROM students;
正确写法要带 OVER:
SELECT name, score, ROW_NUMBER() OVER (PARTITION BY class ORDER BY score DESC) AS rn FROM students;
-
PARTITION BY class表示按班级分组独立编号 -
ORDER BY score DESC决定组内排名顺序,降序才能让高分排前面 - 别名
rn后续过滤要用,不能省略
取每组前N名必须用子查询或 CTE 包裹,WHERE 不能直接跟在 ROW_NUMBER() 后
因为窗口函数在 SQL 执行顺序中晚于 WHERE,所以不能这样写:
SELECT * FROM students WHERE ROW_NUMBER() OVER (...) <= 3; -- 语法错误
得先算出排名,再在外面筛:
SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn FROM employees ) t WHERE t.rn <= 3;
- 子查询或 CTE 是唯一可靠方式;CTE 更易读,尤其当逻辑变复杂时
- 注意别名层级:子查询里定义的
rn,外层才能引用 - 如果用
RANK()或DENSE_RANK(),重复值处理不同,ROW_NUMBER()严格保证不重复编号
ORDER BY 中字段有 NULL 时,排名可能不符合直觉
默认情况下,大多数数据库(如 PostgreSQL、SQL Server)把 NULL 当作最大值处理,ORDER BY score DESC 会让 NULL 排最前——这通常不是你想要的“前N名”。
解决方法是显式控制 NULL 位置:
ROW_NUMBER() OVER (PARTITION BY category ORDER BY score DESC NULLS LAST)
-
NULLS LAST(PostgreSQL、Oracle 支持)或NULLS FIRST - MySQL 8.0+ 也支持该语法;旧版 MySQL 需用
ORDER BY score IS NULL, score DESC曲线救国 - 如果不处理,可能把一堆
NULL当成“最高分”占掉前N名额
性能关键点:PARTITION BY 和 ORDER BY 字段必须有索引
当数据量大时,ROW_NUMBER() 的开销集中在分组和排序阶段。没索引会导致全表扫描 + 临时文件排序,慢得明显。
理想索引应覆盖两个维度:
CREATE INDEX idx_dept_salary ON employees (dept, salary DESC);
- 复合索引顺序很重要:
PARTITION BY字段在前,ORDER BY字段在后 - 如果还经常按其他字段过滤(比如
WHERE status = 'active'),可考虑把该字段加到索引最左侧 - 执行计划里看到
WindowAgg节点耗时高,第一反应就是查索引是否命中
真正难的不是写出语法,而是确认数据分布、NULL 处理策略和索引是否真起效——这三个地方一漏,结果就偏,性能就崩。

















