Need to write recursive date function in sql? - sql

I'm having one holiday table based on that I need to return last working date.
Table_holiday
Id Date Text
1 2013-03-29 Good Friday
2 2013-05-01 Maharashtra day
3 2013-05-02 Holiday
When I execute my date_recursive function it should have to check and return me last_business date
For example
If I execute it on #date datetime = '2013-03-29' it should return me last working date '2013-03-28'as this is the last working date
any help m newbie in sql.

Please try:
DECLARE #Table_holiday as TABLE(id int, [date] datetime, [Text] nvarchar(50))
insert into #Table_holiday values(1, '2013-03-29', 'Good Friday')
insert into #Table_holiday values(2, '2013-05-01', 'Maharashtra day')
insert into #Table_holiday values(3, '2013-05-02', 'Holiday')
declare #date datetime
set #date ='2013-03-29'
;with T(dt) as
(
select #date union all
select T1.[date] from T inner join #Table_holiday T1 on T1.[date]=T.dt-1
)select min(dt)-1 from T

Assuming ANY date not in your holiday table is a working date, you can search backwards until you find a non-holiday.
;with cte(adate) as (
select #date
from table_holiday
where #date = Date
union all
select h.Date
from cte
join table_holiday h on dateadd(d,-1,cte.adate) = h.Date
)
select isnull((select dateadd(d,-1,min(adate)) from cte), #date);

select (case when DATEDIFF(day,GETDATE(),'2008-08-05')<2 Then "last
working date" Else "" END) from Table_holiday
Hope its helpful.

Related

SQL - Insert all dates within range from table into another

I'm looking to populate a table with dates, based upon values contained within another.
Source : tblA
dtFrom dtTo
2019-01-01 2019-01-03
2019-02-01 2019-02-02
2019-03-01 2019-03-01
Destination : tblB
sDate
2019-01-01
2019-01-02
2019-01-03
2019-02-01
2019-02-02
2019-03-01
SQL Server 2014. As always, thanks in advance :-)
You can use a recursive CTE:
with dates as (
select dtfrom as dt, dtto
from tblA
union all
select dateadd(day, 1, dt), dtto
from dates
where dt < dtto
)
insert tblB (sDate)
select distinct dt
from dates;
The select distinct is only necessary to handle overlapping periods. If you know there are no overlaps, then don't use it.
You can use union to combine values from both columns into one rowset:
insert tblB
(sDate)
select distinct dt
from (
select dtFrom as dt
from tblA
union all
select dtTo
from tblA
) s
Use the always handy Calendar Table, which is a table that holds 1 row for each day, for all days between specific years. You can add additional columns like IsBusinessDay or WorkingStartHour / WorkingEndHour to make your date queries much easier.
-- Create Calendar Table
DECLARE #StartDate DATE = '2000-01-01'
DECLARE #EndDate DATE = '2050-01-01'
SET DATEFIRST 1 -- 1: Monday, 7: Sunday
CREATE TABLE CalendarTable (
Date DATE PRIMARY KEY,
IsWorkingDay BIT
-- Other columns you might need
)
;WITH RecursiveCTE AS
(
SELECT
Date = #StartDate
UNION ALL
SELECT
Date = DATEADD(DAY, 1, R.Date)
FROM
RecursiveCTE AS R
WHERE
DATEADD(DAY, 1, R.Date) <= #EndDate
)
INSERT INTO CalendarTable (
Date,
IsWorkingDay)
SELECT
Date = R.Date,
IsWorkingDay = CASE WHEN DATEPART(WEEKDAY, R.Date) BETWEEN 1 AND 5 THEN 1 ELSE 0 END
FROM
RecursiveCTE AS R
OPTION
(MAXRECURSION 0)
Now with your calendar table, just join with a BETWEEN and INSERT to your destination table. You can use DISTINCT to make sure dates don't repeat:
INSERT INTO tblB (
sDate)
SELECT DISTINCT
sDate = C.Date
FROM
tlbA AS A
INNER JOIN CalendarTable AS C ON C.Date BETWEEN A.dtFrom AND A.dtTo
Let's say for example that you only want to insert records that are working days (monday to friday). You just need to filter the calendar table and done. You can add whichever logic you want on your table and just filter it when using, without repeating complex datetime logics.
INSERT INTO tblB (
sDate)
SELECT DISTINCT
sDate = C.Date
FROM
tlbA AS A
INNER JOIN CalendarTable AS C ON C.Date BETWEEN A.dtFrom AND A.dtTo
WHERE
C.IsWorkingDay = 1
With a Calendar you can inner join on the ranges to produce an Insert statement.
DECLARE #StartDate DATETIME = (SELECT MIN(dtFrom) FROM tblA)
DECLARE #EndDate DATETIME = (SELECT MAX(dtTo) FROM tblB)
;WITH Calendar as
(
SELECT CalendarDate = #StartDate, CalendarYear = DATEPART(YEAR, #StartDate), CalendarMonth = DATEPART(MONTH, #StartDate)
UNION ALL
SELECT CalendarDate = DATEADD(MONTH, 1, CalendarDate), CalendarYear = DATEPART(YEAR, CalendarDate), CalendarMonth = DATEPART(MONTH, CalendarDate)
FROM Calendar WHERE DATEADD (MONTH, 1, CalendarDate) <= #EndDate
)
INSERT INTO tblB
SELECT DISTINCT
C.CalendarDate
FROM
Calendar C
INNER JOIN tblA A ON C.CalendarDate BETWEEN A.dtFrom AND A.dtTo
You can achieve this result by using the below queries.
Steps 1 - Create a Custom Function which will take date range as a parameter and will return date series.
CREATE FUNCTION [dbo].[GenerateDateRange]
(#StartDate AS DATE,
#EndDate AS DATE,
#Interval AS INT
)
RETURNS #Dates TABLE(DateValue DATE)
AS
BEGIN
DECLARE #CUR_DATE DATE
SET #CUR_DATE = #StartDate
WHILE #CUR_DATE <= #EndDate BEGIN
INSERT INTO #Dates VALUES(#CUR_DATE)
SET #CUR_DATE = DATEADD(DAY, #Interval, #CUR_DATE)
END
RETURN;
END;
Step 2 - Join this custom function with your table tblA and insert the record in tblb as needed
insert tblb
select b.* from tblA a cross apply dbo.GenerateDateRange(a.dtFrom, a.dtTo, 1) b

Based on day fetch all dates - sql

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)

group by day with missing days

I have a sql server table with the following structure and data:
Created , keyword
'2017-10-03 19:18:00', 'test7'
'2017-10-07 01:06:00', 'test3'
'2017-10-07 15:19:00', 'test2'
'2017-10-07 21:39:00', 'test10'
'2017-10-08 00:36:00', 'test3'
'2017-10-08 01:26:00', 'test13'
'2017-10-08 01:33:00', 'test9'
'2017-10-08 08:23:00', 'test13'
'2017-10-08 09:35:00', 'test9'
'2017-10-08 12:38:00', 'test9'
'2017-10-08 15:07:00', 'test2'
'2017-10-10 05:09:00', 'test4'
I would like to run a query that counts activity and group it by day, also accounting for days when nothing was recorded and show the activity for those days as zero. As such I want a query that will return the result set below:
'2017-10-03', 1
'2017-10-04', 0
'2017-10-05', 0
'2017-10-06', 0
'2017-10-07', 3
'2017-10-08', 7
'2017-10-09', 0
'2017-10-03', 1
I know how to run a query and group it by count for days, but not how to account for days nothing was recorded. As I am new to Sql, I would really appreciate it if someone can provide a working example. Thanks in advance
Try this:
declare #startDate date = '2017-10-01'
declare #endDate date = '2017-10-31'
;with cte as (
select cast(#startDate as date) [dayOfYear]
union all
select DATEADD(day, 1, [dayOfYear]) from cte
where [dayOfYear] < #endDate
)
select dayOfYear, SUM(case when Created is null then 0 else 1 end) from cte
left join MY_TABLE [T] on cte.dayOfYear = CAST(T.Created as date)
group by dayOfYear
The logic is as follows:
get table with all days between #startDate and #endDate (the CTE - I specified first and last of October). Then we left join your table and when the days has no match, we define corresponding value to 0, 1 otherwise. Then it's enough to sum these values day-wise.
Here is a solution when you don't have calendar table:
select row_number() over(order by getdate()) - 1 as n
into #nums
from sys.columns cross join sys.columns c2;
declare #t table(Created datetime, keyword varchar(100));
insert into #t values
('2017-10-03 19:18:00', 'test7'),
('2017-10-07 01:06:00', 'test3'),
('2017-10-07 15:19:00', 'test2'),
('2017-10-07 21:39:00', 'test10'),
('2017-10-08 00:36:00', 'test3'),
('2017-10-08 01:26:00', 'test13'),
('2017-10-08 01:33:00', 'test9'),
('2017-10-08 08:23:00', 'test13'),
('2017-10-08 09:35:00', 'test9'),
('2017-10-08 12:38:00', 'test9'),
('2017-10-08 15:07:00', 'test2'),
('2017-10-10 05:09:00', 'test4')
declare #min_dt date, #max_dt date;
select #min_dt = min(Created), #max_dt = max(Created)
from #t;
with calendar as
(
select dateadd(day, n, #min_dt) as dt
from #nums
where dateadd(day, n, #min_dt) <= #max_dt
)
select c.dt, isnull(count(t.keyword), 0) as cnt
from calendar c left join #t t
on c.dt = cast(t.Created as date)
group by c.dt;
In my case I don't have table calendar but I have fixed table with the numbers (Nums), but if you don't have even table of numbers you can generate it as I did in #nums (you should limit the numbers generated to a reasonable number)
declare #date int
WITH CTE_DatesTable
AS
(
SELECT CAST('20171003' as date) AS [date]
UNION ALL
SELECT DATEADD(dd, 1, [date])
FROM CTE_DatesTable
WHERE DATEADD(dd, 1, [date]) <= '20171010'
)
SELECT [CalendarDate]=[date] into #DimDate FROM CTE_DatesTable
OPTION (MAXRECURSION 0);
select * from #DimDate
This will create a calendar table to join with your current table to fill the gaps

include weekends on sql query

I have a database that stores the log-in and log-out of the employees but we don't have work on weekends. My supervisor want the DTR report format(I'm using RDLC report) include the weekends. (see attached image)
The image above is the expected output format for DTR. I just want to know how to include Weekends though my data are on weekdays only. Is it possible to do this using SQL Query? If yes, should I use looping in sql here?
SQL Code:
select user_id,log_date,login_time,logout_time
from table_DTR
where user_id = 'USER1'
AND log_date BETWEEN '11/21/2014' AND '12/09/2014'
Use common table expression and generate date range with from and to date and than use CTE as left join to actual table. I haven't used user_id filter in left join so apply it to your query:
DECLARE #TMEP TABLE
(
[Date] DATE,
[IN] VARCHAR(10),
[OUT] VARCHAR(10)
)
INSERT INTO #TMEP VALUES ('2014-11-11','7:30','5:30')
INSERT INTO #TMEP VALUES ('2014-11-12','7:30','5:30')
INSERT INTO #TMEP VALUES ('2014-11-13','7:30','5:30')
INSERT INTO #TMEP VALUES ('2014-11-14','7:30','5:30')
INSERT INTO #TMEP VALUES ('2014-11-15','7:30','5:30')
INSERT INTO #TMEP VALUES ('2014-11-18','7:30','5:30')
INSERT INTO #TMEP VALUES ('2014-11-19','7:30','5:30')
INSERT INTO #TMEP VALUES ('2014-11-20','7:30','5:30')
INSERT INTO #TMEP VALUES ('2014-11-21','7:30','5:30')
INSERT INTO #TMEP VALUES ('2014-11-22','7:30','5:30')
INSERT INTO #TMEP VALUES ('2014-11-25','7:30','5:30')
INSERT INTO #TMEP VALUES ('2014-11-26','7:30','5:30')
INSERT INTO #TMEP VALUES ('2014-11-27','7:30','5:30')
INSERT INTO #TMEP VALUES ('2014-11-28','7:30','5:30')
INSERT INTO #TMEP VALUES ('2014-11-29','7:30','5:30')
INSERT INTO #TMEP VALUES ('2014-12-1','7:30','5:30')
INSERT INTO #TMEP VALUES ('2014-12-2','7:30','5:30')
INSERT INTO #TMEP VALUES ('2014-12-3','7:30','5:30')
INSERT INTO #TMEP VALUES ('2014-12-4','7:30','5:30')
DECLARE #FromDate DATE
SET #FromDate = '2014-11-11 06:00:00.000'
DECLARE #ToDate DATE
SET #ToDate = '2014-12-11 06:00:00.000'
;WITH CTE_TableDate ([CTEDate]) as
(
SELECT #FromDate
UNION ALL
SELECT DATEADD(DAY,1,CTEDate) FROM CTE_TableDate WHERE [CTEDate] < #ToDate
)
SELECT
CTE_TableDate.CTEDate,
CASE WHEN DATEPART(DW, CTE_TableDate.CTEDate) = 7 THEN 'SATURDAY'
WHEN DATEPART(DW, CTE_TableDate.CTEDate) = 1 THEN 'SUNDAY'
ELSE TEMP.[In] END AS [IN],
CASE WHEN DATEPART(DW, CTE_TableDate.CTEDate) = 7 THEN 'SATURDAY'
WHEN DATEPART(DW, CTE_TableDate.CTEDate) = 1 THEN 'SUNDAY'
ELSE TEMP.[OUT] END AS [OUT]
FROM CTE_TableDate
LEFT JOIN
(
select
[Date],
[IN],
[OUT]
from
#TMEP) TEMP
ON
CTE_TableDate.CTEDate = TEMP.[Date]
try below solution :
DECLARE #startdate DATE = '11/21/2014' -- your start date
DECLARE #enddate DATE = '12/09/2014' -- your start date
-- create list of all dates between min(log_date) and MAX(log_date)
;WITH cte
AS (SELECT #startdate AS log_date
UNION ALL
SELECT Dateadd(dd, 1, log_date) log_date
FROM cte
WHERE log_date < #enddate)
-- select the data using left outer join so that it will return missing dates too.
SELECT t1.user_id,
c.log_date,
t2.login_time,
t2.logout_time
FROM cte c
CROSS JOIN (SELECT DISTINCT user_id
FROM mytable) t1
LEFT OUTER JOIN mytable t2
ON t2.user_id = t1.user_id
AND t2.log_date = c.log_date
ORDER BY t1.user_id,c.log_date
OPTION(maxrecursion 1000)
It will return null in time columns for weekends.
Note : if you are getting error : The statement terminated. The maximum recursion 100 has been exhausted before statement completion. then try using OPTION(maxrecursion 3000) or greater.
You can create a Calendar table as below:
CREATE TABLE dbo.Calendar
(
dt DATE PRIMARY KEY, -- use SMALLDATETIME if < SQL Server 2008
IsWorkDay BIT
);
DECLARE #s DATE, #e DATE;
SELECT #s = '2000-01-01' , #e = '2029-12-31';
INSERT dbo.Calendar(dt, IsWorkDay)
SELECT DATEADD(DAY, n-1, '2000-01-01'), 1
FROM
(
SELECT TOP (DATEDIFF(DAY, #s, #e)+1) ROW_NUMBER()
OVER (ORDER BY s1.[object_id])
FROM sys.all_objects AS s1
CROSS JOIN sys.all_objects AS s2
) AS x(n);
SET DATEFIRST 1;
-- weekends
UPDATE dbo.Calendar SET IsWorkDay = 0
WHERE DATEPART(WEEKDAY, dt) IN (6,7);
-- Christmas
UPDATE dbo.Calendar SET IsWorkDay = 0
WHERE MONTH(dt) = 12
AND DAY(dt) = 25
AND IsWorkDay = 1;
and then use the same as
DECLARE #table_DTR TABLE
( USER_ID VARCHAR(10),
log_date DATE,
login_time TIME,
logout_time TIME)
INSERT INTO #table_DTR VALUES ('USER1','11/21/2014','7:55:00','5:00:00')
select CASE d.IsWorkDay WHEN 0 THEN datename(dw,d.dt) else DTR.user_id END AS user_id,
d.dt AS log_date,
DTR.login_time,
DTR.logout_time
from dbo.Calendar d
LEFT JOIN #table_DTR DTR ON d.dt = DTR.log_date AND DTR.user_id = 'USER1'
WHERE d.dt BETWEEN '11/21/2014' AND '11/26/2014'
For detailed explanation on pros of Calendar table you can refer here..

Easiest way to populate a temp table with dates between and including 2 date parameters

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