row_number()在mysql 8.0+中必须显式指定order by,否则报错“window 'w' requires an order by clause”;不可省略或用无序伪方案,应选用有索引的稳定字段(如id)排序以兼顾性能与确定性。

ROW_NUMBER() 不支持无序排列,强行省略 ORDER BY 会直接报错 —— 想“快速生成行号”不能跳过排序逻辑,只能选一个确定、稳定、开销小的排序依据。
MySQL 8.0+ 必须写 ORDER BY,空 OVER() 语法非法
在 MySQL 8.0 及以上版本,ROW_NUMBER() OVER () 会触发明确报错:Window 'w' requires an ORDER BY clause。它不接受“按自然顺序”这种模糊语义,也不允许靠执行计划猜行序。
- 想语法通过,必须显式写
ORDER BY,哪怕只是ORDER BY (SELECT NULL)(但 MySQL 不支持该写法) - PostgreSQL 允许
OVER (),但结果顺序由扫描路径决定,VACUUM 后可能变,生产环境不可靠 - SQL Server 和 Oracle 同样要求
ORDER BY,否则报错或拒绝执行
替代方案:用主键或时间戳做轻量排序,而非“无序”
所谓“无序”,其实是想避开业务字段排序开销,又不想引入随机性。这时应选一个**有索引、高选择性、单调递增倾向强**的字段,让排序既快又稳:
-
ORDER BY id:如果id是自增整型主键,B+ 树索引可高效扫描,几乎零额外排序成本 -
ORDER BY created_at, id:时间戳字段常带索引,加id兜底去重,避免重复时间导致序号抖动 - 避免
ORDER BY RAND():每次执行都重排,序号全乱,且严重拖慢查询 - 避免
ORDER BY some_text_column:无索引时触发 filesort,大数据量下极慢
别被“快速”误导:变量方案在 MySQL 8.0+ 已不推荐
有人想绕开 ORDER BY,回头用 MySQL 5.7 的变量写法:@row := @row + 1。但在 MySQL 8.0+ 中,这种写法不仅语义模糊,还受优化器影响,某些 sql_mode 下会失效,且无法并行执行。
- 变量初始化与查询混写(如
FROM t, (SELECT @row := 0) r)在 STRICT 模式下可能被拒绝 - 即使成功,变量计数依赖执行顺序,而 JOIN 或 WHERE 条件可能改变实际求值流
- 窗口函数是优化器友好的,
ROW_NUMBER()能利用索引完成排序,变量方案做不到
真正需要“不排序的行号”,说明设计意图错了
如果你坚持认为“行号就该按插入物理顺序来”,那得警惕:数据库不保证 INSERT 顺序 = SELECT 返回顺序,尤其在并发写入、MVCC、页分裂后。所谓“物理顺序”本身就不稳定。
-
ctid(PostgreSQL)、%%physloc%%(SQL Server)看似可行,但 vacuum / rebuild 后失效,不能用于任何需复现的场景 - 视图里用
ROW_NUMBER()生成序号,调用方再加WHERE或JOIN,会导致序号重算——你以为的“第 5 行”,其实只是当前子集里的第 5 行 - 如果业务真要唯一、持久、全局递增编号,请用
AUTO_INCREMENT、SERIAL或序列对象,而不是在查询层硬凑











