1 28/11/2011 ...
How do I write a function in SQL to implement the above pattern?
You could do something like this in SQL Server:
DECLARE #BaseDate DATE = '20111107';
DECLARE #EndDate DATE = GETDATE(); --Or the "end of dates in the database"
WITH RecursiveCTE AS (
SELECT
1 AS [Counter],
#BaseDate AS [MyDate]
UNION ALL
SELECT
[Counter] + 1,
DATEADD(DAY, 7, MyDate)
FROM
RecursiveCTE
WHERE
MyDate < #EndDate)
SELECT * FROM RecursiveCTE OPTION (MAXRECURSION 0);
To handle dates that aren't exact and make this into a function you would do this:
--Function definition
CREATE FUNCTION SuperDuperDataCalculator (
#BaseDate DATE = '20131016',
#EndDate DATE = '20131020')
RETURNS #Results TABLE (
[Counter] INT,
[Date] DATE)
AS
BEGIN
WITH RecursiveCTE AS (
SELECT
1 AS [Counter],
#BaseDate AS [MyDate]
UNION ALL
SELECT
[Counter] + 1,
CASE WHEN DATEADD(DAY, 7, MyDate) > #EndDate THEN #EndDate ELSE DATEADD(DAY, 7, MyDate) END
FROM
RecursiveCTE
WHERE
MyDate < #EndDate)
INSERT INTO
#Results
SELECT * FROM RecursiveCTE OPTION (MAXRECURSION 0);
RETURN;
END;
GO
--Usage
SELECT * FROM SuperDuperDataCalculator('20131016', '20131020');
--Results
Counter Date
1 2013-10-16
2 2013-10-20
Note that we have to use a multi-statement table-valued function as there is a bug in SQL Server where it won't let you use OPTIONs in a simple table-valued function. The alternative would be to remove the OPTION (MAXRECURSION 0) from the function and remember to use this every time you reference it (i.e. a pretty poor alternative).
...and finally, if you wanted to just return the maximum counter value you could rewrite this as a scalar-valued function, i.e.:
--Function definition
CREATE FUNCTION SuperDuperDataCalculator (
#BaseDate DATE = '20131016',
#EndDate DATE = '20131020')
RETURNS INT
AS
BEGIN
DECLARE #Results TABLE (
[Counter] INT,
[Date] DATE);
DECLARE #ReturnValue INT;
WITH RecursiveCTE AS (
SELECT
1 AS [Counter],
#BaseDate AS [MyDate]
UNION ALL
SELECT
[Counter] + 1,
CASE WHEN DATEADD(DAY, 7, MyDate) > #EndDate THEN #EndDate ELSE DATEADD(DAY, 7, MyDate) END
FROM
RecursiveCTE
WHERE
MyDate < #EndDate)
INSERT INTO
#Results
SELECT * FROM RecursiveCTE OPTION (MAXRECURSION 0);
SELECT #ReturnValue = MAX([Counter]) FROM #Results;
RETURN #ReturnValue;
END;
GO
SELECT dbo.SuperDuperDataCalculator('20131016', '20131020');
Try this - It will get all the weeks and assign a rownumber in the subquery. Then only select the records where row number = 1 because there might be more results for that week. So hence RowNo = 1
SELECT ROW_NUMBER() OVER(ORDER BY RowNo) AS IncrementalWeek,dte
FROM
(
SELECT DISTINCT DATEPART(ww,CONVERT(VARCHAR(20),createdDate,111)) AS [week],
CONVERT(VARCHAR(20),createdDate,111) AS dte,
ROW_NUMBER() OVER(PARTITION BY DATEPART(ww,Convert(VARCHAR(20),createdDate,111)) ORDER BY DATEPART(ww,CONVERT(VARCHAR(20),createdDate,111))) AS RowNo
FROM YourTable
) AS tble
WHERE RowNo = 1
ORDER BY [week]
Related
I have start date, end date and name of days. How can fetch all dates between those two dates of that specific days in sql?
example data:
start_date:4/11/2018
end_date: 5/11/2018
days: monday, thursday
expected output: all dates between start and end date which comes on monday and thursday and store them in table
updated
my present code(not working)
; WITH CTE(dt)
AS
(
SELECT #P_FROM_DATE
UNION ALL
SELECT DATEADD(dw, 1, dt) FROM CTE
WHERE dt < #P_TO_DATE
)
INSERT INTO Table_name
(
ID
,DATE_TIME
,STATUS
,CREATED_DATE
,CREATED_BY
)
SELECT #P_ID
,(SELECT dt FROM CTE WHERE DATENAME(dw, dt) In ('tuesday','friday',null))
,'NOT SENT'
,CAST(GETDATE() AS DATE)
,#USER_ID
Another approach for generating dates between ranges can be like following query. This will be faster compared to CTE or WHILE loop.
DECLARE #StartDate DATETIME = '2018-04-11'
DECLARE #EndDate DATETIME = '2018-05-15'
SELECT #StartDate + RN AS DATE FROM
(
SELECT (ROW_NUMBER() OVER (ORDER BY (SELECT NULL)))-1 RN
FROM master..[spt_values] T1
) T
WHERE RN <= DATEDIFF(DAY,#StartDate,#EndDate)
AND DATENAME(dw,#StartDate + RN) IN('Monday','Thursday')
Note:
If the row count present in master..[spt_values] is not sufficient for the provided range, you can make a cross join with the same to get a bigger range like following.
SELECT (ROW_NUMBER() OVER (ORDER BY (SELECT NULL)))-1 RN
FROM master..[spt_values] T1
CROSS JOIN master..[spt_values] T2
By this you will be able to generate date between a range with gap of 6436369 days.
You can use a recursive common table expression (CTE) to generate a list of days. With datepart(dw, ...) you can filter for specific days of the week.
An example that creates a list of Mondays and Thursdays between March 1st and today:
create table ListOfDates (dt date);
with cte as
(
select cast('2018-03-01' as date) as dt -- First day of interval
union all
select dateadd(day, 1, dt)
from cte
where dt < getdate() -- Last day of interval
)
insert into ListOfDates
(dt)
select dt
from cte
where datepart(dw, dt) in (2, 5) -- 2=Monday and 5=Thursday
option (maxrecursion 0)
See it working at SQL Fiddle.
This will work for you:
DECLARE #table TABLE(
ID INT IDENTITY(1,1),
Date DATETIME,
Day VARCHAR(50)
)
DECLARE #Days TABLE(
ID INT IDENTITY(1,1),
Day VARCHAR(50)
)
INSERT INTO #Days VALUES ('Monday')
INSERT INTO #Days VALUES ('Thursday')
DECLARE #StartDate DATETIME='2018-01-01';
DECLARE #EndDate DATETIME=GETDATE();
DECLARE #Day VARCHAR(50)='Friday';
DECLARE #TempDate DATETIME=#StartDate;
WHILE CAST(#TempDate AS DATE)<=CAST(#EndDate AS DATE)
BEGIN
IF EXISTS (SELECT 1 FROM #Days WHERE DAY IN (DATENAME(dw,#TempDate)))
BEGIN
INSERT INTO #table
VALUES (
#TempDate, -- Date - datetime
DATENAME(dw,#TempDate) -- Day - varchar(50)
)
END
SET #TempDate=DATEADD(DAY,1,#TempDate)
END
SELECT * FROM #table
INSERT INTO TargetTab(dateCOL)
SELECT dateCOL
FROM tab
WHERE dateCOL >= startdate AND dateCOL <= enddate
AND (DATENAME(dw,dateCOL) ='Thursday' OR DATENAME(dw,dateCOL) = 'Monday')
Try this query to get your result.
Use a recursive CTE to generate your dates, then filter by week day.
SET DATEFIRST 1 -- 1: Monday, 7 Sunday
DECLARE #StartDate DATE = '2018-04-11'
DECLARE #EndDate DATE = '2018-05-15'
DECLARE #WeekDays TABLE (WeekDayNumber INT)
INSERT INTO #WeekDays (
WeekDayNumber)
VALUES
(1), -- Monday
(4) -- Thursday
;WITH GeneratingDates AS
(
SELECT
GeneratedDate = #StartDate,
WeekDay = DATEPART(WEEKDAY, #StartDate)
UNION ALL
SELECT
GeneratedDate = DATEADD(DAY, 1, G.GeneratedDate),
WeekDay = DATEPART(WEEKDAY, DATEADD(DAY, 1, G.GeneratedDate))
FROM
GeneratingDates AS G -- Notice that we are referencing a CTE that we are also declaring
WHERE
G.GeneratedDate < #EndDate
)
SELECT
G.GeneratedDate
FROM
GeneratingDates AS G
INNER JOIN #WeekDays AS W ON G.WeekDay = W.WeekDayNumber
OPTION
(MAXRECURSION 30000)
Try this:
declare #start date = '04-11-2018'
declare #end date = '05-11-2018'
declare #P_ID int = 1
declare #USER_ID int = 11
;with cte as(
select #start [date]
union all
select dateadd(DAY, 1, [date]) from cte
where [date] < #end
)
--if MY_TABLE doesn't exist
select #P_ID,
[date],
'NOT SENT',
cast(getdate() as date),
#USER_ID
into MY_TABLE
from cte
--here you can specify days: 1 - Sunday, 2 - Monday, etc.
where DATEPART(dw,[date]) in (2, 5)
option (maxrecursion 0)
--if MY_TABLE does exist
--insert into MY_TABLE
--select #P_ID,
-- [date],
-- 'NOT SENT',
-- cast(getdate() as date),
-- #USER_ID
--from cte
--where DATEPART(dw,[date]) in (2, 5)
--option (maxrecursion 0)
I am new here and new to SQL. I got this tip to create a scalar function that extends the functionality of the built-in DateAdd function (namely to exclude weekends and holidays). It is working fine for a single date but when I use it on a table, it is extremely slow.
I have seen some recommendation to use inline table-valued function instead. Would anyone be so kind to point me in the direction, how I would go about converting the below to inline table-valued function? I greatly appreciate it.
ALTER FUNCTION [dbo].[CalcWorkDaysAddDays]
(#StartDate AS DATETIME, #Days AS INT)
RETURNS DATE
AS
BEGIN
DECLARE #Count INT = 0
DECLARE #WorkDay INT = 0
DECLARE #Date DATE = #StartDate
WHILE #WorkDay < #Days
BEGIN
SET #Count = #Count - 1
SET #Date = DATEADD(DAY, #Count, #StartDate)
IF NOT (DATEPART(WEEKDAY, #Date) IN (1,7) OR
EXISTS (SELECT * FROM RRCP_Calendar WHERE Is_Holiday = 1 AND Calendar_Date = #Date))
BEGIN
SET #WorkDay = #WorkDay + 1
END
END
RETURN #Date
END
This should do the trick...
CREATE FUNCTION dbo.tfn_CalcWorkDaysAddDays
(
#StartDate DATETIME,
#Days INT
)
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
SELECT
TheDate = MIN(x.Calendar_Date)
FROM (
SELECT TOP (#Days)
c.Calendar_Date
FROM
dbo.RRCP_Calendar c
WHERE
c.Calendar_Date < #StartDate
AND c.Is_Holiday = 0
AND c.is_Weekday = 1 -- this should be part of your calendar table. do not calculate on the fly.
ORDER BY
c.Calendar_Date DESC
) x;
GO
Note: for best performance, you'll want a unique, filtered, nonclustered index on on your calendar table...
CREATE UNIQUE NONCLUSTERED INDEX uix_RRCPCalendar_CalendarDate_IsHoliday_isWeekday ON dbo.RRCP_Calendar (
Calendar_Date, Is_Holiday, is_Weekday)
WHERE Is_Holiday = 0 AND is_Weekday = 1;
Try this and see if it returns the same values as your function, just without the loop:
SELECT WorkDays =
DATEADD(WEEKDAY, #Days, #StartDate) -
(SELECT COUNT(*)
FROM RRCP_Calendar
WHERE Is_Holiday = 1
AND Calendar_Date >= #StartDate
AND Calendar_Date <= DATEADD(DAY, #Days, #StartDate)
)
And yes, you can sometimes get substantially better performance with a non-procedural table-valued-function, but you have to set it up right. Look up SARGability and non-procedural table-valued-functions for more info, but if the above query works, this should do the trick:
CREATE FUNCTION dbo.SelectWorkDaysAddDays(#StartDate DATE, #Days INT)
RETURNS TABLE
AS
RETURN
SELECT WorkDays =
DATEADD(WEEKDAY, #Days, #StartDate) -
(SELECT COUNT(*)
FROM RRCP_Calendar
WHERE Is_Holiday = 1
AND Calendar_Date >= #StartDate
AND Calendar_Date <= DATEADD(DAY, #Days, #StartDate)
)
GO
And then you call the function by using an OUTER APPLY join:
SELECT y.foo
, y.bar
, dt.WorkDays
FROM dbo.YourTable y
OUTER APPLY dbo.SelectWorkDaysAddDays(#StartDate, #Days) dt
Say [dbo].[CalcWorkDaysAddDays], getdate(), 2 would return Sept 8,
2017 since it is adding two days. This function is similar to DateAdd
but it is excluding weekends and holidays
The code you've posted doesn't do this.
But if you want the result described, the function can be smth like this:
alter FUNCTION [dbo].[CalcWorkDaysAddDays_inline](#StartDate As DateTime,#Days AS INT)
returns table
as return
with cte as
(
select *,
ROW_NUMBER() over(order by Calendar_Date) as rn
from RRCP_Calendar
where Calendar_Date > #StartDate and #Days > 0
and not (DATEPART(WEEKDAY,Calendar_Date) IN (1,7) or Is_Holiday = 1)
union ALL
select *,
ROW_NUMBER() over(order by Calendar_Date desc) as rn
from RRCP_Calendar
where Calendar_Date < #StartDate and #Days < 0
and not (DATEPART(WEEKDAY,Calendar_Date) IN (1,7) or Is_Holiday = 1)
)
select cast(Calendar_Date as date) as dt
from cte
where rn = abs(#Days);
I am new to sql, I have created a CTE now I want to get the count of rows from CTE result set
DECLARE #start_date date,#end_date DATE ;
select #start_date= min(ETA) from [dbo].[testTable]
select #end_date=max(ETA) from [dbo].[testTable];
;WITH AllDays
AS (
SELECT #start_date AS [Date]
--, 1 AS [level]
UNION ALL
SELECT DATEADD(DAY, 1, [Date])
--, [level] + 1
FROM AllDays
WHERE [Date] < #end_date )
--Insert into #tempETA (CallETA)
SELECT [Date]--, [level]
FROM AllDays OPTION (MAXRECURSION 0)
select count(a.Date),a.Date from AllDays a
I am getting error here:
(1048 row(s) affected)
Msg 208, Level 16, State 1, Line 20
Invalid object name 'AllDays'.
A CTE can hold temporary result set that is defined within the execution scope of a single SELECT, INSERT, UPDATE, DELETE. You cannot have another SELECT outside the scope of CTE
Use ##ROWCOUNT to get the count of CTE. Considering you want the CTE result and its count.
SELECT [Date]--, [level]
FROM AllDays OPTION (MAXRECURSION 0)
select ##ROWCOUNT
If you want to count to be part of your result then use COUNT() OVER()
SELECT [Date],count(1)over() as Total_count
FROM AllDays OPTION (MAXRECURSION 0)
Check out this below code and tell.is this what you want to get.?
DECLARE #start_date date,#end_date DATE ;
select #start_date= min(ETA) from [dbo].[testTable]
select #end_date=max(ETA) from [dbo].[testTable];
;WITH AllDays
AS (
SELECT #start_date AS [Date]
--, 1 AS [level]
UNION ALL
SELECT DATEADD(DAY, 1, #start_date)
--, [level] + 1
FROM [testTable]
WHERE #start_date < #end_date )
--Insert into #tempETA (CallETA)
--SELECT [Date]--, [level]
--FROM AllDays OPTION (MAXRECURSION 0)
select count(a.Date) from AllDays a
I'm trying to insert yearly weekend details such as date, dayName into a SQL Server table using the following stored procedure
alter procedure usp_AddOfficeHolidays
#paramName NVARCHAR(max)
as
begin
DECLARE #Year AS INT,
#FirstDateOfYear DATETIME,
#LastDateOfYear DATETIME
-- You can change #year to any year you desire
SELECT #year = 2016
SELECT #FirstDateOfYear = DATEADD(yyyy, #Year - 1900, 0)
SELECT #LastDateOfYear = DATEADD(yyyy, #Year - 1900 + 1, 0)
-- Creating Query to Prepare Year Data
--declare dayN varchar(max)
if (select COUNT(*) from tblWeekSettings) < 1
begin
;WITH cte AS
(
SELECT
1 AS DayID,
#FirstDateOfYear AS FromDate,
DATENAME(dw, #FirstDateOfYear) AS Dayname
UNION ALL
SELECT
cte.DayID + 1 AS DayID,
DATEADD(d, 1 ,cte.FromDate),
DATENAME(dw, DATEADD(d, 1 ,cte.FromDate)) AS Dayname
FROM cte
WHERE DATEADD(d, 1, cte.FromDate) < #LastDateOfYear
)
SELECT FromDate AS Date, Dayname
FROM CTE
WHERE DayName IN (SELECT Param FROM dbo.fn_MVParam(#paramName,','))
OPTION (MaxRecursion 370)
end
else
begin
Select 'Exists'
end
end
and executing it using
exec usp_AddOfficeHolidays 'Saturday,Sunday'
which returns the following result
This works perfectly fine, BUT I have been unable to add/insert these details into the following table
I face the following error when I try to access the WEEKEND details by its alias CTE
The statement terminated. The maximum recursion 100 has been exhausted
before statement completion
Although I've added the clause
OPTION (MaxRecursion 370)
suggested by these links which I found on stack overflow
The maximum recursion 100 has been exhausted before statement completion
The statement terminated. The maximum recursion 100 has been exhausted before statement completion
EDIT
Basically i face the specified error when i try something like this
alter procedure usp_AddOfficeHolidays
#paramName NVARCHAR(max)
as
begin
----------------------------------------------------------
DECLARE #Year AS INT,
#FirstDateOfYear DATETIME,
#LastDateOfYear DATETIME
-- You can change #year to any year you desire
SELECT #year = 2016
SELECT #FirstDateOfYear = DATEADD(yyyy, #Year - 1900, 0)
SELECT #LastDateOfYear = DATEADD(yyyy, #Year - 1900 + 1, 0)
-- Creating Query to Prepare Year Data
--declare dayN varchar(max)
if (select COUNT(*) from tblWeekSettings) < 1
begin
;WITH cte AS (
SELECT 1 AS DayID,
#FirstDateOfYear AS FromDate,
DATENAME(dw, #FirstDateOfYear) AS Dayname
UNION ALL
SELECT cte.DayID + 1 AS DayID,
DATEADD(d, 1 ,cte.FromDate),
DATENAME(dw, DATEADD(d, 1 ,cte.FromDate)) AS Dayname
FROM cte
WHERE DATEADD(d,1,cte.FromDate) < #LastDateOfYear
)
SELECT FromDate AS Date, Dayname
FROM CTE
WHERE DayName IN(SELECT Param FROM dbo.fn_MVParam(#paramName,','))
insert into tblWeekSettings(DayNo,WeekDayName,Dates)
values('',Dayname,Date)
OPTION (MaxRecursion 370)
end
else
begin
Select 'Exists'
end
--select cte
-----------------------------------------------------------
end
Any sort of help here would really be appreciated! I just need to insert the data in my the specified table!
Thank you!
That's the error:
SELECT FromDate AS Date, Dayname
FROM CTE
WHERE DayName IN(SELECT Param FROM dbo.fn_MVParam(#paramName,','))
I split this code to make you understand what code actually is working in this case:
insert into tblWeekSettings(DayNo,WeekDayName,Dates)
values('',Dayname,Date)
OPTION (MaxRecursion 370)
OPTION (MAX RECURSION) now belongs to single insert statement. Which is standalone, totally not related to CTE.
You actually need this, I suppose:
;with CTE (...)
insert into tblWeekSettings(DayNo,WeekDayName,Dates)
SELECT FromDate AS Date, Dayname
FROM CTE
WHERE DayName IN(SELECT Param FROM dbo.fn_MVParam(#paramName,','))
OPTION (MaxRecursion 370)
but there are three columns in target table whilst your select has only two columns. So you'll have to update your select.
Some tips about INSERT-SELECT:
http://www.w3schools.com/sql/sql_insert_into_select.asp
this code:
insert into tblWeekSettings(DayNo,WeekDayName,Dates)
values('',Dayname,Date)
does not have any source for inserting. This is not valid code - you don't have here any Dayname,Date variables - they are not even referenced with # as variables. It's totally not valid code.
For anyone who face the following issues
Getting Weeked details i.e. DayName, Date
Inserting into a Table
This stored procedure would do the trick.
alter procedure usp_AddOfficeHolidays
#paramName NVARCHAR(max)
as
begin
----------------------------------------------------------
DECLARE #Year AS INT,
#FirstDateOfYear DATETIME,
#LastDateOfYear DATETIME
-- You can change #year to any year you desire
SELECT #year = 2016
SELECT #FirstDateOfYear = DATEADD(yyyy, #Year - 1900, 0)
SELECT #LastDateOfYear = DATEADD(yyyy, #Year - 1900 + 1, 0)
-- Creating Query to Prepare Year Data
--declare dayN varchar(max)
if (select COUNT(*) from tblWeekSettings) < 1
begin
;WITH cte AS (
SELECT 1 AS DayID,
#FirstDateOfYear AS FromDate,
DATENAME(dw, #FirstDateOfYear) AS Dayname
UNION ALL
SELECT cte.DayID + 1 AS DayID,
DATEADD(d, 1 ,cte.FromDate),
DATENAME(dw, DATEADD(d, 1 ,cte.FromDate)) AS Dayname
FROM cte
WHERE DATEADD(d,1,cte.FromDate) < #LastDateOfYear
)
insert into tblWeekSettings(DayNo,Dates,WeekDayName)
SELECT '',FromDate AS Date, Dayname
FROM CTE WHERE DayName IN(SELECT Param FROM dbo.fn_MVParam(#paramName,','))
OPTION (MaxRecursion 30000)
end
else
begin
Select 'Exists'
end
--select cte
-----------------------------------------------------------
end
Plus this article could really be helpful.
http://www.w3schools.com/sql/sql_insert_into_select.asp
Try this, I hope this is useful for you.
alter procedure usp_AddOfficeHolidays
#paramName NVARCHAR(max)
as
begin
----------------------------------------------------------
DECLARE #Year AS INT,#DayNo as int=1,
#FirstDateOfYear DATETIME,
#LastDateOfYear DATETIME
-- You can change #year to any year you desire
SELECT #year = 2016
SELECT #FirstDateOfYear = DATEADD(yyyy, #Year - 1900, 0)
SELECT #LastDateOfYear = DATEADD(yyyy, #Year - 1900 + 1, 0)
Select getdate() DateOfYear into #tbl where 1=0
-- Creating Query to Prepare Year Data
--declare dayN varchar(max)
if (select COUNT(*) from tblWeekSettings) < 1
begin
while (#FirstDateOfYear< #LastDateOfYear)
begin
Insert Into #tbl (DayNo,DateOfYear) values (#DayNo,#FirstDateOfYear)
set #FirstDateOfYear+=1
set #DayNo+=1;
End
Insert Into tblWeekSettings (DayNo,WeekDayName,Dates)
SELECT DayNo,DATENAME(dw, DateOfYear) Name,DateOfYear AS Date
FROM #tbl
WHERE DATENAME(dw, DateOfYear) IN(SELECT Param FROM dbo.fn_MVParam(#paramName,','))
end
else
begin
Select 'Exists'
end
--select cte
-----------------------------------------------------------
end
What is the easiest way to populate a temp table with dates including and between 2 date parameters. I only need the 1st day of the month dates.
So for example if #StartDate = '2011-01-01' and #EndDate = '2011-08-01'
Then I want this returned in the table
2011-01-01
2011-02-01
2011-03-01
2011-04-01
2011-05-01
2011-06-01
2011-07-01
2011-08-01
This works even if the #StartDate is not the first of the month. I'm assuming that if it's not the start of the month, you want to begin with the first of the next month. Otherwise remove the +1.:
;WITH cte AS (
SELECT CASE WHEN DATEPART(Day,#StartDate) = 1 THEN #StartDate
ELSE DATEADD(Month,DATEDIFF(Month,0,#StartDate)+1,0) END AS myDate
UNION ALL
SELECT DATEADD(Month,1,myDate)
FROM cte
WHERE DATEADD(Month,1,myDate) <= #EndDate
)
SELECT myDate
FROM cte
OPTION (MAXRECURSION 0)
declare #StartDate date = '2014-01-01';
declare #EndDate date = '2014-05-05';
;WITH cte AS (
SELECT #StartDate AS myDate
UNION ALL
SELECT DATEADD(day,1,myDate) as myDate
FROM cte
WHERE DATEADD(day,1,myDate) <= #EndDate
)
SELECT myDate
FROM cte
OPTION (MAXRECURSION 0)
declare #StartDate datetime
declare #EndDate datetime
select #StartDate = '2011-01-01' , #EndDate = '2011-08-01'
select #StartDate= #StartDate-(DATEPART(DD,#StartDate)-1)
declare #temp table
(
TheDate datetime
)
while (#StartDate<=#EndDate)
begin
insert into #temp
values (#StartDate )
select #StartDate=DATEADD(MM,1,#StartDate)
end
select * from #temp
Works even if the #StartDate is not the first day of the month by going back to the initial day of the month of StartDate
this is tested in SQL 2008 R2
Declare #StartDate datetime = '2015-03-01'
Declare #EndDate datetime = '2015-03-31'
declare #temp Table
(
DayDate datetime
);
WHILE #StartDate <= #EndDate
begin
INSERT INTO #temp (DayDate) VALUES (#StartDate);
SET #StartDate = Dateadd(Day,1, #StartDate);
end ;
select * from #temp
Result:
DayDate
-----------------------
2015-03-01 00:00:00.000
2015-03-02 00:00:00.000
2015-03-03 00:00:00.000
2015-03-04 00:00:00.000
...
Interestingly, it is faster to create from enumerated data as per this article.
DECLARE #StartDate DATE = '10001201';
DECLARE #EndDate DATE = '20000101';
DECLARE #dim TABLE ([date] DATE)
INSERT #dim([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
ORDER BY
s1.[object_id]
) AS x
) AS y;
On my machine, it's around 60% faster with large date ranges. The recursion method can populate 2000 years worth of data in around 3 seconds though, and looks a lot nicer, so I don't really recommend this method just for incrementing days.
Correction for null dates:
IF OBJECT_ID('tempdb..#dim') IS NOT NULL
DROP TABLE #dim
CREATE TABLE #dim ([date] DATE)
if not #Begin_Date is null and not #End_Date is null
begin
INSERT #dim([date])
SELECT d
FROM(
SELECT
d = DATEADD(DAY, rn - 1, #Begin_Date)
FROM
(
SELECT TOP (DATEDIFF(DAY, #Begin_Date, #End_Date))
rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
FROM
sys.all_objects AS s1
CROSS JOIN
sys.all_objects AS s2
ORDER BY
s1.[object_id]
) AS x
) AS y;
end
CREATE TABLE #t (d DATE)
INSERT INTO #t SELECT GETDATE()
GO
INSERT #t SELECT DATEADD(DAY, -1, MIN(d)) FROM #t
GO 10