postgresql用generate_series()补全缺失日期最快最稳,无需递归;需显式指定date/timestamp类型和步长(如'1 day'),避免类型错误;sql server可用master..spt_values(type='p')或cte;mysql 8.0+依赖with recursive但有1000层默认限制。

PostgreSQL 用 generate_series() 最快最稳
不用写递归函数——generate_series() 就是专干这事的,性能好、语法直白、还支持日期步长。它不是“递归函数”,但效果一样,而且更可靠。
常见错误是硬套 WITH RECURSIVE 去生成几百上千天,结果栈溢出或慢得离谱。其实绝大多数场景根本不需要手写递归。
-
SELECT generate_series('2024-01-01'::date, '2024-01-10'::date, '1 day')直接返回 10 行日期 - 步长可换:
'7 days'生成每周一,'1 month'生成月初 - 注意类型必须显式转换,否则可能报错:
ERROR: function generate_series(timestamp without time zone, timestamp without time zone, unknown) does not exist - 返回列默认叫
generate_series,可 alias 成dt或date_val方便后续 JOIN
SQL Server 怎么办:用 master..spt_values 或 CTE
SQL Server 没内置日期序列函数,master..spt_values 是老但有效的取巧方式,比 WITH RECURSIVE 更快且无深度限制。
容易踩的坑是直接用 number 表生成日期却忘了过滤类型(type = 'P'),导致结果混入无关行。
- 安全写法:
SELECT DATEADD(day, number, '2024-01-01') AS dt FROM master..spt_values WHERE type = 'P' AND number BETWEEN 0 AND 9 - 如果不能访问
master数据库(比如云 SQL Server),就得用 CTE:WITH dates AS (SELECT CAST('2024-01-01' AS date) AS dt UNION ALL SELECT DATEADD(day, 1, dt) FROM dates WHERE dt ,但必须加 <code>OPTION (MAXRECURSION 1000),否则默认只跑 100 层 - CTE 递归在 SQL Server 里对 >5000 行序列容易超内存或超时,别硬扛
MySQL 8.0+ 只能靠 WITH RECURSIVE,但有硬限制
MySQL 没类似 generate_series 的函数,WITH RECURSIVE 是唯一选择,但它默认最多递归 1000 层,生成超过 1000 天会直接报错:Recursive query aborted after 1000 iterations。
别急着调大 cte_max_recursion_depth——这会影响整个会话甚至全局,且不解决性能问题。
- 临时改上限(仅当前连接):
SET SESSION cte_max_recursion_depth = 5000; - 递归体必须严格满足终止条件,比如
WHERE dt ,不能写成 <code> 再靠 LIMIT 截断,否则仍会触发深度检查 - 起始值和步长要统一类型:
DATE_ADD(dt, INTERVAL 1 DAY)比dt + 1更安全,后者在某些模式下会转成整数加法 - 超过 2000 行建议拆成多个小段 union,或者导出后用脚本补全
跨数据库兼容写法?别强求,按需选型
想写一条 SQL 跑遍 PostgreSQL / MySQL / SQL Server?基本做不到。各厂对递归、序列、日期计算的支持差异太大,强行抽象只会增加 bug 和维护成本。
真正该做的是:明确目标数据库、数据量级、是否需要索引、是否要 JOIN 其他表——这些决定了你该用哪个方案,而不是先选“看起来通用”的写法。
比如在报表场景中,如果只是补全缺失日期做左连接,PostgreSQL 用 generate_series;SQL Server 用 spt_values;MySQL 就老老实实设好 recursion depth 写 CTE。多一行适配代码,远比后期调优省事。











