子查询不能直接生成序列表,需用递归cte或用户变量;递归cte(with recursive)是跨库通用、符合标准的推荐方案,如生成1到100:select n from (with recursive seq(n) as (select 1 union all select n+1 from seq where n

子查询不能直接生成序列表,得靠递归或变量模拟
SQL 标准里没有“生成序列表”的内置函数,SELECT 子查询本身不维护行序,也不自动编号。想得到 1, 2, 3, ..., n 这样的临时序列,必须借助数据库特有机制——不是所有子查询都能干这事,硬写 (SELECT 1 UNION SELECT 2 ...) 属于静态枚举,不“动态”。
常见可行路径只有两条:递归 CTE(推荐) 或 用户变量(MySQL 5.7/8.0 兼容性差)。PostgreSQL 和 SQL Server 原生支持递归;MySQL 8.0+ 支持标准 CTE,但 5.7 只能靠变量或临时表凑合。
用递归 CTE 生成连续整数序列(跨库通用写法)
这是目前最干净、可读性高、且符合 SQL 标准的方案。关键在 WITH RECURSIVE 和终止条件控制。
- 起始值和上限必须明确指定,比如生成 1 到 100:
SELECT n FROM (WITH RECURSIVE seq(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM seq WHERE n - MySQL 8.0+、PostgreSQL、SQL Server 都支持,但语法微调:SQL Server 用
OPTION (MAXRECURSION n)防爆栈;PostgreSQL 要加MAXRECURSION参数(默认 100),超限需显式设置 - 性能上,递归深度超过几千行时明显变慢,别用它生成百万级序列——真要大范围,走应用层或数字辅助表更稳
MySQL 5.7 怎么绕过无 CTE 限制?变量法有坑
MySQL 5.7 不支持 WITH RECURSIVE,有人用 @row := @row + 1 模拟,但必须配合 SELECT 的执行顺序保证,而该顺序在无 ORDER BY 时不可靠。
- 安全写法是先构造一个有确定顺序的驱动表(哪怕只有一列),再关联变量赋值:
SELECT @row := @row + 1 AS n FROM (SELECT 1 UNION SELECT 2 UNION SELECT 3) AS dummy, (SELECT @row := 0) AS init - 变量初始化必须在同一语句内完成,拆成两条
SET @row=0再SELECT在某些客户端(如 PHP PDO)会失效 - 该方式在 MySQL 8.0 中已被标记为“不推荐”,且开启
sql_mode=ONLY_FULL_GROUP_BY时可能报错
为什么不该用 JOIN 多次子查询拼序列?
有人试图用 SELECT a.n FROM (SELECT 0 n UNION SELECT 1) a JOIN (SELECT 0 n UNION SELECT 1) b ON 1=1 来做笛卡尔积生成 0–3,这方法扩展性极差,且极易失控。
- 每多一层 JOIN,行数指数增长:
2^k,k=10 就是 1024 行,k=20 直接破百万,根本不是“动态”而是“爆炸式静态” - 无法灵活控制上下界,只能靠手工 UNION 或嵌套,可维护性为零
- 优化器对这种无关联 JOIN 常放弃统计信息,执行计划不稳定,尤其在大表关联时拖慢整个查询
真正需要动态长度序列时,递归 CTE 是事实标准;若环境受限,优先考虑建一张小的 numbers 辅助表(含 0–10000),比任何运行时生成都快也可靠。变量法仅作临时调试用,上线前务必替换。











