ROW_NUMBER() 是跨数据库生成有序序列的标准方法,需配合确定性排序(如ORDER BY dummy)和基础行集(如VALUES或CROSS JOIN)来稳定产生1~100等整数序列,并以此推导日期、订单号、状态等模拟数据。

ROW_NUMBER() 本身不生成测试数据,它只给已有结果集编号。想用它“模拟数据”,本质是构造一个有规律的行序列,再配合计算逻辑生成值——比如日期、ID、状态等。
为什么直接用 ROW_NUMBER() 构造序列最常用
ROW_NUMBER() 构造序列最常用因为它是标准、跨库(MySQL 8.0+、PostgreSQL、SQL Server、Oracle)、无需临时表或变量的稳定方案。比 @row := @row + 1(MySQL 5.7 及以前)或递归 CTE 更轻量、更可预测。
- 数据库不提供
GENERATE_SERIES()(PostgreSQL 有,但 MySQL 没)时,ROW_NUMBER()是事实上的替代方案 - 只要能写出一个“至少有 N 行”的基础结果集,就能靠
OVER (ORDER BY ...)控制编号顺序 - 常见 trick:从系统表(如 PostgreSQL 的
pg_class)或大表(如日志表)里 LIMIT 出足够行数;更稳妥的是用VALUES或UNION ALL手动凑几行
如何用 ROW_NUMBER() 生成 1~100 的整数序列
ROW_NUMBER() 生成 1~100 的整数序列这是所有模拟数据的起点。关键点:必须有确定的排序依据,且不能依赖原表物理顺序(不可靠)。
- PostgreSQL / SQL Server / Oracle:可安全用
ORDER BY (SELECT NULL)或ORDER BY ctid(PG) - MySQL 8.0+:不支持
ORDER BY (SELECT NULL),必须显式列;推荐用ORDER BY RAND()(仅用于生成,不关心顺序)或虚构列 - 最通用写法:用
VALUES构造最小驱动集,再ROW_NUMBER()
WITH t AS ( SELECT 1 AS dummy UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 ) SELECT ROW_NUMBER() OVER (ORDER BY dummy) AS n FROM t t1 CROSS JOIN t t2 -- 5×5=25 行,再加一层可到 125 LIMIT 100;
生成带业务含义的模拟数据:日期、订单号、用户状态
拿到 n 后,直接算:日期 = DATE_ADD('2024-01-01', INTERVAL n DAY),订单号 = CONCAT('ORD-', LPAD(n, 6, '0')),状态 = CASE WHEN n % 3 = 0 THEN 'pending' WHEN n % 3 = 1 THEN 'shipped' ELSE 'delivered' END。
- 注意时区和日期函数差异:
DATEADD()(SQL Server)、DATE_ADD()(MySQL)、current_date + n * interval '1 day'(PostgreSQL) - 避免用
ORDER BY RAND()做主排序——它会让ROW_NUMBER()结果每次执行都不同,破坏可重现性 - 如果需要“每组固定数量”,比如 10 个用户各 5 条订单,就用
PARTITION BY user_id ORDER BY n,再嵌套生成
容易被忽略的稳定性陷阱
模拟数据一旦用于测试脚本或 CI 环境,行号漂移会导致断言失败。根本原因不是 ROW_NUMBER() 本身,而是 ORDER BY 不够唯一。
- 错误写法:
ROW_NUMBER() OVER (ORDER BY id)—— 如果id是自增但被删过,空洞存在,n就不连续 - 正确做法:用一个**确定性、无空洞、可复现**的排序键,例如
ORDER BY n(来自上面生成的序号),或ORDER BY (SELECT 0)(部分数据库允许) - 在 MySQL 中,
ORDER BY (SELECT 0)仍可能报错;此时只能接受用ORDER BY n套一层子查询,确保编号严格按预期顺序
实际用起来,最稳的路径就是:先用最小集合生成确定性序号列,再基于它做所有推导。别指望数据库“自动给你整齐的 1 到 100”。

















