row_number()重置序号的关键是partition by;它定义编号从1重新开始的边界,必须显式指定分区列(如来源表标识或业务维度),仅order by无法重置,全局连续编号需配合partition by实现分组内独立编号。

ROW_NUMBER() 重置序号的关键是 PARTITION BY
直接在多表 JOIN 后用 ROW_NUMBER() OVER (ORDER BY ...),序号会全局连续,不会按表或分组重置。真正能“重置”的唯一方式是显式指定 PARTITION BY——它定义了序号从 1 重新开始的边界。
常见错误现象:ROW_NUMBER() OVER (ORDER BY id) 跑完 UNION ALL 的三张表,结果是 1,2,3,...,1000,而不是每张表内都从 1 开始。
- 必须把“要各自重置”的维度(比如来源表名、分类字段、时间分区)作为
PARTITION BY的列 - 如果原始表没带来源标识,
UNION ALL时得手动加一列,例如SELECT *, 'orders' AS src FROM orders -
PARTITION BY列值相同的所有行,才共享一个独立的编号序列
UNION ALL 后重置:先打标再分区
合并不同结构或不同来源的表时,不能依赖表名自动识别,必须人工注入可分区的标记列。否则 ROW_NUMBER() 看不到逻辑分组依据。
典型场景:把 users、admins、guests 三张表垂直合并,要求每类用户内部编号从 1 起。
- 写法必须是:
SELECT *, 'users' AS role FROM users UNION ALL SELECT *, 'admins' AS role FROM admins UNION ALL SELECT *, 'guests' AS role FROM guests - 之后再套一层查询:
SELECT *, ROW_NUMBER() OVER (PARTITION BY role ORDER BY created_at) AS rn FROM (...) - 别在子查询里就用
ROW_NUMBER(),否则窗口函数作用域被限制在单表内,UNION ALL后失效
JOIN 多表后按业务维度重置:别错把关联键当分区键
如果已经 JOIN 了订单、用户、商品三张表,想“按每个用户的订单”分别编号(即每个 user_id 下订单号从 1 开始),PARTITION BY 必须是 user_id,不是 order_id 或 product_id。
容易踩的坑:ROW_NUMBER() OVER (PARTITION BY order_id ORDER BY create_time) —— 每个 order_id 只有一行,结果全是 1。
- 确认分区字段在结果集中有实际区分度:查一下
SELECT COUNT(*), COUNT(DISTINCT user_id) FROM joined_result - 如果 JOIN 导致某用户出现多行(比如一对多),且你希望“每个用户+每个商品组合”单独编号,那就
PARTITION BY user_id, product_id - ORDER BY 子句决定同一分区内编号顺序,不加可能产生非确定结果(尤其在分布式数据库中)
性能与兼容性注意点
ROW_NUMBER() OVER (PARTITION BY ...) 在大数据量下可能变慢,因为需要对每个分区单独排序。MySQL 8.0+、PostgreSQL、SQL Server 都支持,但 SQLite 和旧版 MySQL 不支持窗口函数。
- 如果只是想给结果集加行号(不要求按组重置),用
LIMIT/OFFSET或应用层计数更轻量 - 分区字段上建索引能显著加速,尤其是
PARTITION BY a ORDER BY b这种组合,建议建联合索引(a, b) - 某些数据库(如 Hive)对窗口函数的内存消耗敏感,分区太多(比如百万级不同
user_id)可能 OOM,这时得考虑预聚合或改用 MAPREDUCE 方式
最常被忽略的是:PARTITION BY 的表达式必须和 SELECT 列里的字段完全一致——不能是别名,也不能是计算式(除非所有数据库都支持该语法,比如 PostgreSQL 允许 PARTITION BY UPPER(name),但 MySQL 不行)。写之前先查文档。











