row_number()在mysql 8.0+中必须配合over()子句使用,且over内必须含order by(不可省略或为空),支持partition by分组编号,但不支持where直接引用别名,需嵌套查询过滤。

ROW_NUMBER() 在 MySQL 8.0+ 中可以直接用,但必须配合窗口函数语法
MySQL 8.0 是第一个原生支持窗口函数的版本,ROW_NUMBER() 不再需要靠变量模拟。但很多人写完发现报错或结果不连续,根本原因是没加 OVER() 子句——它不是可选的,是强制要求。
常见错误现象:ERROR 1064 (42000): You have an error in your SQL syntax,通常是因为漏了 OVER 或括号不匹配。
-
ROW_NUMBER()必须写成ROW_NUMBER() OVER (ORDER BY ...),ORDER BY不能省;如果只想要物理顺序,可用主键或时间字段兜底,比如ORDER BY id - 不支持
OVER ()空括号(即无排序),会报错ERROR 3589 (HY000): Window '<unnamed>' lacks an ORDER BY clause</unnamed> - 如果表本身无主键、无稳定排序字段,
ROW_NUMBER()的“连续性”可能每次执行不一致——这不是 bug,是窗口函数按排序逻辑生成序号的必然行为
按业务分组后各自编号,用 PARTITION BY + ORDER BY
比如给每个用户最近 3 条订单分别标上 1/2/3,这时不能只靠全局 ORDER BY created_at,否则序号跨用户混排。得用 PARTITION BY user_id 切分作用域。
示例语句:
SELECT user_id, order_id, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn FROM orders WHERE created_at >= '2024-01-01';
-
PARTITION BY字段值相等的行归为一组,每组内独立编号,从 1 开始 - 同一组内若
ORDER BY字段有重复值(如多个订单同秒创建),ROW_NUMBER()仍会强制赋予不同序号(1,2,3…),不会并列;如需并列用RANK()或DENSE_RANK() - 注意
PARTITION BY后不能跟表达式(如PARTITION BY YEAR(created_at)),MySQL 8.0.20+ 才支持函数表达式,旧版会报错
和 LIMIT 连用时序号可能被截断,别误以为是函数失效
有人写 SELECT ..., ROW_NUMBER() OVER (...) AS rn FROM t ORDER BY x LIMIT 10,发现 rn 只有 1–10,就怀疑没生效。其实没错——LIMIT 是在窗口函数计算之后执行的,序号已生成,只是只返回前 10 行。
- 如果想取“每组第 1 条”,正确做法是嵌套查询:
SELECT * FROM (SELECT ..., ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...) AS rn FROM t) t1 WHERE rn = 1 - 直接在外部加
WHERE rn = 1会报错,因为WHERE执行早于窗口函数,此时rn还不存在 - 性能注意:窗口函数会在内存中缓存中间结果,大数据量下
PARTITION BY分组过多(如百万级不同 user_id)可能触发sort_buffer_size不足,查SHOW WARNINGS看是否有Warning | 1287 | 'window function' is deprecated类提示(实际是内存告警)
替代方案:低版本 MySQL 或复杂排序场景慎用变量模拟
虽然问题限定 MySQL 8.0,但实践中常遇到迁移老系统或需要兼容 5.7 的情况。此时有人用 @rownum := @rownum + 1 模拟,但极易出错。
- MySQL 8.0 默认禁用变量赋值在 SELECT 中的副作用(
sql_mode含ONLY_FULL_GROUP_BY时更明显),可能返回全 0 或乱序 - 变量方式无法天然支持
PARTITION BY,要手动判断分组字段变化,代码冗长且难维护 - 真正需要兼容旧版时,优先考虑应用层编号,或升级到 8.0+ 再用原生窗口函数——别为了少改 SQL 去踩变量的坑
最易被忽略的一点:窗口函数的排序字段如果有 NULL,默认按 NULLS FIRST 处理(MySQL 8.0.22+ 支持显式声明 NULLS LAST),而旧版一律把 NULL 排最前,可能导致序号 1 落在意外行上。检查数据质量前,先确认 NULL 的分布和预期位置。











