MySQL 8.0 的 ROW_NUMBER() 必须带 OVER() 且其中必须含 ORDER BY,不可单独使用或省略排序;它用于分组内编号,需配合子查询过滤,不支持 WHERE 直接引用别名,性能依赖 PARTITION BY 和 ORDER BY 字段的索引。

MySQL 8.0 的 ROW_NUMBER() 必须带 OVER(),不能单独使用
很多人一上来就写 SELECT ROW_NUMBER(), name FROM users;,直接报错:ERROR 1064 (42000): You have an error in your SQL syntax。这是因为 ROW_NUMBER() 是窗口函数,不是普通标量函数——它必须搭配 OVER() 子句定义计算范围,否则语法不合法。
常见错误还包括漏写 ORDER BY:即使只用 PARTITION BY,OVER() 内也必须有 ORDER BY,否则 MySQL 会拒绝执行(8.0.2+ 版本严格校验)。
-
ROW_NUMBER() OVER (PARTITION BY user_no ORDER BY create_date DESC)✅ 合法 -
ROW_NUMBER() OVER (PARTITION BY user_no)❌ 报错:Missing ORDER BY in window specification -
ROW_NUMBER() OVER (ORDER BY create_date)✅ 合法(全表视为一个分区)
分组内最新记录怎么取?用 ROW_NUMBER() 替代变量 @x/@y
老版本常用用户变量模拟序号,比如 @x:=IF(@y=user_no, @x+1, 1),但这种写法在 MySQL 8.0+ 中已被标记为“不推荐”,且在并行查询、优化器重排下结果不可靠。
用 ROW_NUMBER() 更安全直观:
SELECT * FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY user_no ORDER BY create_date DESC) AS rn
FROM order_info
) t WHERE rn = 1;
注意点:
- 排序字段必须明确包含
DESC才能拿到“最新”;默认是ASC,会取最早一条 - 如果
create_date有重复,ROW_NUMBER()仍会强制分配不同序号(1,2,3…),不保证稳定性;需加二级排序,如ORDER BY create_date DESC, order_id DESC - 不能在
WHERE中直接引用rn别名——窗口函数在WHERE阶段尚未计算,必须套子查询或 CTE
PARTITION BY 和 GROUP BY 不是一回事,别混用
PARTITION BY 只是划分“窗口”,不聚合、不减少行数;而 GROUP BY 会折叠行、要求 SELECT 列要么在 GROUP BY 中,要么被聚合函数包裹。
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
例如想查每个用户的订单数 + 每条订单的序号,不能这么写:
SELECT user_no, COUNT(*), ROW_NUMBER() OVER (...) -- ❌ 混合 GROUP BY 和窗口函数,语法报错
正确做法是分开处理:
- 用
COUNT(*) OVER (PARTITION BY user_no)算每用户的订单总数(保留所有行) - 用
ROW_NUMBER() OVER (PARTITION BY user_no ORDER BY order_id)算每条订单在该用户内的序号 - 两者可同时出现在同一
SELECT中,互不干扰
性能和索引影响:ORDER BY 字段最好走索引
ROW_NUMBER() 的排序开销是实打实的。如果 OVER (PARTITION BY a ORDER BY b) 中的 b 没有索引,MySQL 就得对每个分区做文件排序(Using filesort),数据量大时延迟明显。
建议操作:
- 对高频使用的
PARTITION BY + ORDER BY组合建联合索引,比如(user_no, create_date) - 避免在
ORDER BY中用函数或表达式,如ORDER BY DATE(create_date)会让索引失效 - 分区字段(
PARTITION BY)本身不要求索引,但若同时用于WHERE条件,单独建索引仍有意义
真正容易被忽略的是:窗口函数的结果不能被索引,所以后续再按 rn = 1 过滤,仍要依赖外层扫描——别指望给 rn 加索引能提速。










