How to create a row for every day in a date range using a stored procedure?
One option is a recursive 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
hint added thanks to Scott Hodgin's comment, below.)
Another option is to use a Table-Valued-Function. This approach is very fast, and offers a little more flexibility. You supply the Date/Time Range, DatePart and Increment. Also offers the advantage of including it in a CROSS APPLY
For Example
Select * from [dbo].[udf-Range-Date]('2017-03-05','2017-04-11','DD',1)
Returns
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
The UDF if Interested
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)
*/
Using Aaron Bertrand's post on how to create a date dimension table as an example, I came up with this:
DECLARE @StartDate DATE ='2017-03-05 00:00:00'
DECLARE @EndDate DATE ='2017-04-11 00:00:00'
Declare @DateTable table ([date] DATE PRIMARY KEY);
-- use the catalog views to generate as many rows as we need
INSERT @DateTable ([date])
SELECT d
FROM (
SELECT d = DATEADD(DAY, rn - 1, @StartDate)
FROM (
SELECT TOP (DATEDIFF(DAY, @StartDate, @EndDate)) rn = ROW_NUMBER() OVER (
ORDER BY s1.[object_id]
)
FROM sys.all_objects AS s1
CROSS JOIN sys.all_objects AS s2
-- on my system this would support > 5 million days
ORDER BY s1.[object_id]
) AS x
) AS y;
SELECT *
FROM @DateTable
ORDER BY [date]
You should be able to put this type of logic in your stored procedure and add whatever else you need.