2017-01-23 52 views
0

我有一個月的範圍表是這樣的:T-SQL生成日期間個月,從表

CREATE TABLE [dbo].[T_month_ranges](
    [StartMonth] [datetime] NULL, 
    [EndMonth] [datetime] NULL 
) ON [PRIMARY] 

INSERT INTO [dbo].[T_month_ranges] ([StartMonth],[EndMonth]) VALUES 
('2015-02-01','2015-04-01') 
INSERT INTO [dbo].[T_month_ranges] ([StartMonth],[EndMonth]) VALUES 
('2016-12-01','2017-02-01') 
INSERT INTO [dbo].[T_month_ranges] ([StartMonth],[EndMonth]) VALUES 
('2017-08-01','2017-09-01'); 

這些日期僅代表年份和月份,第一天在這裏並不重要。

現在我們需要創建select,它返回此表中日期之間的所有月份。所以在查詢結果將是這樣的日期:

2015-02-01;2015-03-01;2015-04-01;2016-12-01;2017-01-01;2017-02-01;2017-08-01;2017-09-01 

什麼是在sql server中實現這一點的最佳方法?

回答

2

您可以使用遞歸CTE:

with m as (
     select mr.startmonth as mon, mr.endmonth 
     from T_month_ranges mr 
     union all 
     select dateadd(month, 1, m.mon), m.endmonth 
     from m 
     where m.mon < m.endmonth 
    ) 
select m.mon 
from m; 

如果你的範圍是真正寬(這似乎不太可能有個月),那麼你可能需要將MAXRECURSION選項設置爲0。

替代 - 這可能是快 - 是用數字表:

with n as (
     select row_number() over (order by (select null)) - 1 as n 
     from master..spt_values 
    ) 
select dateadd(month, n.n, mr.startmonth) 
from T_month_ranges mr join 
    n 
    on dateadd(month, n.n, mr.startmonth) <= mr.endmonth; 
+0

戈登您好,感謝的答案,但是:第一方案的回報:無效的列名稱endmonth「, – nbvcx100

0

我會經常使用TVF來創建動態的日期/時間範圍。一個理貨/桌子也可以做到這一點。該功能參數驅動,您提供的範圍,日期部分和增量

例如:

Select A.* 
     ,B.* 
From T_month_ranges A 
Cross Apply [dbo].[udf-Range-Date](A.StartMonth,A.EndMonth,'MM',1) B 

返回

enter image description here

的UDF如果有興趣。

CREATE FUNCTION [dbo].[udf-Range-Date] (@R1 datetime,@R2 datetime,@Part varchar(10),@Incr int) 
Returns Table 
Return (
    with cte0(M) As (Select 1+Case @Part When 'YY' then DateDiff(YY,@R1,@R2)/@Incr When 'QQ' then DateDiff(QQ,@R1,@R2)/@Incr When 'MM' then DateDiff(MM,@R1,@R2)/@Incr When 'WK' then DateDiff(WK,@R1,@R2)/@Incr When 'DD' then DateDiff(DD,@R1,@R2)/@Incr When 'HH' then DateDiff(HH,@R1,@R2)/@Incr When 'MI' then DateDiff(MI,@R1,@R2)/@Incr When 'SS' then DateDiff(SS,@R1,@R2)/@Incr End), 
     cte1(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)), 
     cte2(N) As (Select Top (Select M from cte0) Row_Number() over (Order By (Select NULL)) From cte1 a, cte1 b, cte1 c, cte1 d, cte1 e, cte1 f, cte1 g, cte1 h), 
     cte3(N,D) As (Select 0,@R1 Union All Select N,Case @Part When 'YY' then DateAdd(YY, N*@Incr, @R1) When 'QQ' then DateAdd(QQ, N*@Incr, @R1) When 'MM' then DateAdd(MM, N*@Incr, @R1) When 'WK' then DateAdd(WK, N*@Incr, @R1) When 'DD' then DateAdd(DD, N*@Incr, @R1) When 'HH' then DateAdd(HH, N*@Incr, @R1) When 'MI' then DateAdd(MI, N*@Incr, @R1) When 'SS' then DateAdd(SS, N*@Incr, @R1) End From cte2) 

    Select RetSeq = N+1 
      ,RetVal = D 
    From cte3,cte0 
    Where D<[email protected] 
) 
/* 
Max 100 million observations -- Date Parts YY QQ MM WK DD HH MI SS 
Syntax: 
Select * from [dbo].[udf-Range-Date]('2016-10-01','2020-10-01','YY',1) 
Select * from [dbo].[udf-Range-Date]('2016-01-01','2017-01-01','MM',1) 
*/ 

選項2(無UDF)

Select A.* 
     ,B.* 
From T_month_ranges A 
Cross Apply (
       Select Top (DateDiff(MM,A.Startmonth,A.EndMonth)+1) 
        Date=DateAdd(MM,Row_Number() over (Order by (Select null)) - 1,A.Startmonth) 
       From master..spt_values 
      ) B