row_number() 必须配合 over() 使用,否则报错;正确语法为over (partition by ... order by ...),其中order by不可省略,partition by可选,且顺序不可颠倒。

ROW_NUMBER() 必须配合 OVER() 使用,否则报错
直接写 SELECT ROW_NUMBER() 会触发 ERROR 1064 (42000),因为 MySQL 8.0 的窗口函数不允许脱离 OVER() 子句独立存在。它不是普通标量函数,必须明确指定排序逻辑和(可选)分组逻辑。
正确写法的核心是:OVER (PARTITION BY ... ORDER BY ...)。其中 PARTITION BY 定义“分组”,ORDER BY 定义组内排序依据 —— 两者缺一不可(ORDER BY 尤其不能省,否则报错)。
-
PARTITION BY可以是单列、多列,甚至表达式,但不能是子查询或非确定性函数(如NOW()) - 如果不需要物理分组,只想要全局连续序号,就省略
PARTITION BY,只留ORDER BY -
ORDER BY中的字段必须在SELECT列表中出现,或属于分组字段(否则在GROUP BY模式下可能报错)
分组排序时,PARTITION BY 和 ORDER BY 的顺序不能颠倒
常见误写是把 ORDER BY 放前面、PARTITION BY 放后面,比如 OVER (ORDER BY score DESC PARTITION BY class_id) —— 这会导致语法错误。MySQL 严格要求 PARTITION BY 在前、ORDER BY 在后。
实际场景示例:给每个班级内学生按成绩降序编号:
SELECT student_id, class_id, score, ROW_NUMBER() OVER (PARTITION BY class_id ORDER BY score DESC) AS rn FROM students;
注意:rn = 1 表示该班最高分,rn = 2 是第二高……同一 class_id 下序号从 1 开始重置。
- 若成绩相同,
ROW_NUMBER()仍强制分配不同序号(比如并列第一会被拆成 1 和 2),需用RANK()或DENSE_RANK()替代 -
PARTITION BY class_id不会改变原始行数,只是为每行计算一个组内序号 - 不能在
WHERE或GROUP BY中直接引用rn别名,必须用子查询或 CTE 包裹后过滤
想筛选每组 top-N,必须用子查询或 CTE 包一层
窗口函数不能出现在 WHERE 子句中,所以不能写 WHERE rn 。直接执行会报 <code>Unknown column 'rn' in 'where clause'。
两种安全做法:
- 用派生表(子查询):外层查
rn,内层算ROW_NUMBER() - 用 CTE(更清晰):先定义带
rn的结果集,再SELECT * FROM cte WHERE rn
CTE 示例(取每班前 2 名):
WITH ranked AS (
SELECT
student_id,
class_id,
score,
ROW_NUMBER() OVER (PARTITION BY class_id ORDER BY score DESC) AS rn
FROM students
)
SELECT student_id, class_id, score
FROM ranked
WHERE rn <p>性能提示:如果 <code>students</code> 表很大,确保 <code>(class_id, score)</code> 有联合索引,否则 <code>ORDER BY</code> 阶段容易触发 filesort。</p><h3>ORDER BY 中包含 NULL 值时,默认排在最前还是最后?</h3><p>MySQL 8.0 默认把 <code>NULL</code> 视为最小值,所以在 <code>ORDER BY score DESC</code> 中,<code>NULL</code> 会排在结果末尾;而 <code>ORDER BY score ASC</code> 时,<code>NULL</code> 排最前。这会影响 <code>ROW_NUMBER()</code> 的分配顺序。</p><p>如果你希望 <code>NULL</code> 统一排最后(无论升序降序),得显式控制:</p>
-
ORDER BY score DESC NULLS LAST(MySQL 8.0.22+ 支持) - 旧版本可用
ORDER BY score IS NULL, score DESC模拟:先按是否为 NULL 排序(false 在前),再按 score 排
漏掉这个细节,可能导致你认为的“最高分”其实是 NULL 行,尤其当业务字段允许为空时容易踩坑。
分组排序本身不难,难的是理解窗口函数的执行时机 —— 它发生在 WHERE、GROUP BY、HAVING 之后,但早于 ORDER BY 和 LIMIT。这个顺序决定了你能对什么数据编号、又不能在哪一步用这个编号。











