Sql-Server

如何使用儲存過程為日期範圍內的每一天創建一行?

  • November 10, 2020

我想創建一個儲存過程,它將在給定日期範圍內的每一天在表中創建一行。儲存過程接受兩個輸入 - 使用者所需日期範圍的開始日期和結束日期。

所以,假設我有一張這樣的桌子:

SELECT Day, Currency
FROM ConversionTable

Day 是 DateTime,而 Currency 只是一個整數。

為簡單起見,假設我總是希望 Currency 列對於這些插入的每一行都為 1。因此,如果有人輸入“2017 年 3 月 5 日”作為開始日期,“2017 年 4 月 11 日”作為結束日期,我希望創建以下行:

2017-03-05 00:00:00, 1
2017-03-06 00:00:00, 1
...
2017-04-11 00:00:00, 1

對儲存過程進行編碼以執行此操作的最佳方法是什麼?我在我的測試環境中使用的是 SQL Server 2008 R2,但是我們的真實環境使用的是 SQL Server 2012,所以如果 2012 年引入了新功能使這項任務變得更容易,我可以升級我的測試機器。

一種選擇是遞歸 CTE:

DECLARE @StartDate datetime = '2017-03-05'
      ,@EndDate   datetime = '2017-04-11'
;

WITH theDates AS
    (SELECT @StartDate as theDate
     UNION ALL
     SELECT DATEADD(day, 1, theDate)
       FROM theDates
      WHERE DATEADD(day, 1, theDate) <= @EndDate
    )
SELECT theDate, 1 as theValue
 FROM theDates
OPTION (MAXRECURSION 0)
;

MAXRECURSION由於下面的 Scott Hodgin 的評論添加了提示。)

另一種選擇是使用表值函式。這種方法非常快,並且提供了更多的靈活性。您提供日期/時間範圍、日期部分和增量。還提供了將其包含在 CROSS APPLY 中的優勢

例如

Select * from [dbo].[udf-Range-Date]('2017-03-05','2017-04-11','DD',1) 

退貨

RetSeq  RetVal
1   2017-03-05 00:00:00.000
2   2017-03-06 00:00:00.000
3   2017-03-07 00:00:00.000
4   2017-03-08 00:00:00.000
5   2017-03-09 00:00:00.000
...
36  2017-04-09 00:00:00.000
37  2017-04-10 00:00:00.000
38  2017-04-11 00:00:00.000

有興趣的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<=@R2
)
/*
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) 
*/

引用自:https://dba.stackexchange.com/questions/166024