如何在SQL Server存储过程中显示当月所有日期(含无记录日期)
解决SQL Server中生成当月全日期报表的问题
这问题我日常开发中经常碰到——核心原因是你现在的查询是以业务表(table1)作为主表,但业务表只有存在数据的日期记录,所以左关联后自然不会显示无数据的日期。要实现全日期输出,你需要先构造一个包含当月所有日期的日期维度数据集,再以此为基础左关联业务表。
方法一:用递归CTE生成临时日期范围(最方便,无需额外表)
递归CTE是SQL Server里生成连续日期的常用方式,不需要预先创建任何辅助表,直接在查询里生成:
DECLARE @FromDate DATE = '2020-01-01'; DECLARE @ToDate DATE = '2020-01-31'; -- 第一步:生成当月所有日期的临时数据集 WITH DateRange AS ( SELECT @FromDate AS ReportDate -- 起始日期 UNION ALL SELECT DATEADD(DAY, 1, ReportDate) -- 逐天递增 FROM DateRange WHERE ReportDate < @ToDate -- 直到达到结束日期 ) -- 第二步:以日期维度为主表,左关联业务表 SELECT dr.ReportDate, -- 保证输出每个日期 a1.f1, a1.f2, b.f3, b.f5 FROM DateRange dr LEFT JOIN table1 a1 ON a1.Date = dr.ReportDate -- 关联业务表的日期到报表日期 LEFT JOIN table2 b ON b.f1 = a1.f1 AND b.Date = dr.ReportDate -- 如果table2也有日期字段,务必关联,避免重复数据 OPTION (MAXRECURSION 0); -- 解除递归次数限制(默认100,当月31天没问题,大日期范围必须加)
方法二:用数字表生成日期(效率更高,适合大日期范围)
如果你有一个预先创建的Numbers表(包含连续数字1到N),可以用这种方式生成日期,性能比递归CTE更好:
DECLARE @FromDate DATE = '2020-01-01'; DECLARE @ToDate DATE = '2020-01-31'; -- 用Numbers表生成日期范围 WITH DateRange AS ( SELECT DATEADD(DAY, n.Number - 1, @FromDate) AS ReportDate FROM Numbers n WHERE DATEADD(DAY, n.Number - 1, @FromDate) <= @ToDate ) -- 关联业务表的逻辑和上面一致 SELECT dr.ReportDate, a1.f1, a1.f2, b.f3, b.f5 FROM DateRange dr LEFT JOIN table1 a1 ON a1.Date = dr.ReportDate LEFT JOIN table2 b ON b.f1 = a1.f1 AND b.Date = dr.ReportDate;
关键注意点
- 主表必须是日期维度:只有把全日期的数据集作为主表,才能保证所有日期都被输出,无数据的字段自动显示
NULL。 - 关联条件要精准:业务表和日期维度的关联必须基于日期字段,否则会出现数据匹配错误或重复。
- 递归CTE的MAXRECURSION:如果你的日期范围超过100天,一定要加上
OPTION (MAXRECURSION 0),否则会触发递归次数超限的错误。
内容的提问来源于stack exchange,提问作者HTML Man
相关产品推荐
相关产品推荐

