How to find dates between two dates in SQL Server 2005 - sql

I need to find the dates between two dates in SQL Server.
where
startdate = '2015-12-04 00:00:00.000'
and enddate = '2015-12-07 00:00:00.000'
Result should be
2015-12-04 00:00:00.000
2015-12-05 00:00:00.000
2015-12-06 00:00:00.000
2015-12-07 00:00:00.000

This will work in sqlserver 2005:
DECLARE #startdate datetime
DECLARE #enddate datetime
SELECT #startdate ='2015-12-04', #enddate='2015-12-07'
;WITH N(N)AS
(SELECT 1 FROM(SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1)M(N)),
tally(N)AS(SELECT ROW_NUMBER()OVER(ORDER BY N.N)FROM N,N a,N b,N c,N d,N e,N f)
SELECT top (datediff(d, #startdate, #enddate) + 1)
dateadd(d, N - 1, #startdate)
FROM tally
Result(I removed timepart):
2015-12-04
2015-12-05
2015-12-06
2015-12-07

Try:
DECLARE #Date1 DATE, #Date2 DATE
SET #Date1 = '20151204'
SET #Date2 = '20151210'
SELECT DATEADD(DAY,number+1,#Date1) [Date]
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(DAY,number+1,#Date1) < #Date2
Or:
declare #startDate date;
declare #endDate date;
select #startDate = '20151204';
select #endDate = '20151210';
with interdate as
(
select dt = dateadd(dd, 1, #startDate)
where dateadd(dd, 1, #startDate) < #endDate
union all
select dateadd(dd, 1, dt)
from interdate
where dateadd(dd, 1, dt) < #endDate
)
select *
from interdate

Related

Is there an efficient way to break a date range into hours per day?

In SQL Server I am attempting to break a date range into hours per day and have the following bit of code which is OK for a short time frame, but rather inefficient for longer periods of time. Could anyone suggest a more efficient approach?
DECLARE #StartDate datetime = '2015-01-27 07:32:35.000',
#EndDate datetime = '2015-04-29 14:39:35.000',
#TempDate datetime = '';
SET #TempDate = #StartDate;
DECLARE #dateTimeTable TABLE (dt datetime, minCol INT);
WHILE #TempDate < #EndDate
BEGIN
INSERT INTO #dateTimeTable VALUES (CONVERT(date,#TempDate), 1)
SET #TempDate = DATEADD(minute,1,#TempDate)
END
Select dt,
FORMAT(SUM(minCol) / 60.0,'F') as Hours
from #dateTimeTable
GROUP BY dt
Thanks,
Carl
The best way would be to use recursive cte :
DECLARE #StartDate datetime = '2015-01-27 07:32:35.000',
#EndDate datetime = '2015-04-29 14:39:35.000';
WITH cte AS (
SELECT CAST(#StartDate AS DATE) startdate,DATEDIFF(minute, #StartDate, DATEADD(DAY, 1, CAST(#StartDate AS DATE) ) ) / 60.0 hours
UNION ALL
SELECT DATEADD(DAY,1, startdate), DATEDIFF(minute, DATEADD(DAY,1, startdate), CASE WHEN DATEADD(DAY,2, startdate) > #EndDate
THEN #enddate ELSE DATEADD(DAY,2, startdate) END) / 60.0
FROM cte
WHERE startdate <> CAST(#EndDate AS DATE)
)
SELECT * FROM cte
db<>fiddle here

SQL Query to find the past 3rd business day excluding holidays and weekends

I am trying to write the sql query to find the past 3rd business day excluding the holidays and weekends. Below is my approachs but it's not giving the correct solution. Please suggest
First approach:
alter function getdate(#givendate datetime)
returns datetime
AS
BEGIN
declare #calcdate datetime
declare #reqdate datetime
declare #count bit
set #count=1
select #calcdate=#givendate-4;
select #reqdate=#calcdate;
while(#calcdate<#givendate)
begin
while(#count=1)
begin
if exists(select 1 from dbo.holidays with(Nolock) where dbo.holidays.date=#reqdate)
begin
select #reqdate=#reqdate-1
end
else
begin
set #count=0
end
end
select #calcdate=#calcdate+1
end
return #reqdate
END
Second approach:
alter function dbo.getpast3day (#date date)
returns datetime
As
begin
declare #reqdate datetime;
declare #Newyears_day datetime;
declare #Martin_Luther_Kin_Birthday datetime;
declare #presidents_day datetime;
declare #Memorial_day datetime;
declare #Independence_day datetime;
declare #Labour_day datetime;
declare #Columbus_day datetime;
declare #Veterans_day datetime;
declare #Thanksgiving_day datetime;
declare #Christmas_day datetime;
SELECT #reqdate= DATEADD(DAY, CASE (DATEPART(WEEKDAY, #date) + ##DATEFIRST) % 7
WHEN 1 THEN -5
WHEN 2 THEN -6
WHEN 3 THEN -6
WHEN 4 THEN -6
WHEN 5 THEN -6
ELSE -4
END, DATEDIFF(DAY, 0, #date)) ;
--#Martin_Luther_Kin_Birthday
begin
WITH dates AS (
SELECT DATEADD(day, number, #date) AS theDate
FROM master.dbo.spt_values
WHERE type = 'P'
)
SELECT #Martin_Luther_Kin_Birthday=theDate
FROM dates
WHERE DATEDIFF(day, '19000101', theDate) % 7 = 0
AND DATEPART(day, thedate)>=15 and DATEPART(day, thedate)<=21 and DATEPART(month,thedate)=1 and datepart(year,thedate)= year(#date);
end
--#presidents_day
begin
WITH dates AS (
SELECT DATEADD(day, number, #date) AS theDate
FROM master.dbo.spt_values
WHERE type = 'P'
)
SELECT #presidents_day=theDate
FROM dates
WHERE DATEDIFF(day, '19000101', theDate) % 7 = 0
AND DATEPART(day, thedate)>=15 and DATEPART(day, thedate)<=21 and DATEPART(month,thedate)=2 and datepart(year,thedate)= year(#date);
end
--#Memorial_day
begin
WITH dates AS (
SELECT DATEADD(day, number, #date) AS theDate
FROM master.dbo.spt_values
WHERE type = 'P'
)
SELECT #Memorial_day=theDate
FROM dates
WHERE DATEDIFF(day, '19000101', theDate) % 7 = 0
AND DATEPART(day, thedate)>=22 and DATEPART(day, thedate)<=31 and DATEPART(month,thedate)=2 and datepart(year,thedate)= year(#date);
end
--#Labour_day
begin
WITH dates AS (
SELECT DATEADD(day, number, #date) AS theDate
FROM master.dbo.spt_values
WHERE type = 'P'
)
SELECT #Labour_day=theDate
FROM dates
WHERE DATEDIFF(day, '19000101', theDate) % 7 = 0
AND DATEPART(day, thedate)>=1 and DATEPART(day, thedate)<=7 and DATEPART(month,thedate)=9 and datepart(year,thedate)= year(#date);
end
--#Columbus_day
begin
WITH dates AS (
SELECT DATEADD(day, number, #date) AS theDate
FROM master.dbo.spt_values
WHERE type = 'P'
)
SELECT #Columbus_day=theDate
FROM dates
WHERE DATEDIFF(day, '19000101', theDate) % 7 = 0
AND DATEPART(day, thedate)>=8 and DATEPART(day, thedate)<=14 and DATEPART(month,thedate)=10 and datepart(year,thedate)= year(#date);
end
--#Thanksgiving_day
begin
WITH dates AS (
SELECT DATEADD(day, number, #date) AS theDate
FROM master.dbo.spt_values
WHERE type = 'P'
)
SELECT #Thanksgiving_day=theDate
FROM dates
WHERE DATEDIFF(day, '19000101', theDate) % 7 = 0
AND DATEPART(day, thedate)>=22 and DATEPART(day, thedate)<=31 and DATEPART(month,thedate)=11 and datepart(year,thedate)= year(#date);
end
if (#reqdate=#Martin_Luther_Kin_Birthday or #reqdate=#presidents_day or #reqdate=#Memorial_day or #reqdate=#Labour_day or #reqdate=#Columbus_day or #reqdate=#Thanksgiving_day)
begin
select #reqdate= DATEADD(day,-6,#reqdate)
end
--RETURN '2216-09-20 00:00:00.000'
return #reqdate
end
Not 100% clear on what past 3rd business day is, but perhaps this can help. Clearly you will have to decide what a holiday is.
Select [dbo].[SomeFunctionName]('2016-01-20',3)
Returns
2016-01-15
The custom function is:
CREATE FUNCTION [dbo].[SomeFunctionName](#Date date,#nDay int)
Returns Date
As
Begin
Declare #RetVal Date
;with cteHolidays as (
Select * from (Values
('2016-01-01','New Year''s Day'),
('2016-01-18','Martin Luther King, Jr,'),
('2016-02-15','Washington''s Birthday'),
('2016-03-25','Good Friday'),
('2016-05-30','Memorial Day'),
('2016-07-04','Independence Day'),
('2016-09-05','Labor Day'),
('2016-11-24','Thanksgiving'),
('2016-11-25','Black Friday'),
('2016-12-26','Christmas Day')
) as Holidays (Date,Name)
), cteDays as (
Select *
,RowNr = Row_Number() over (Order By RetVal Desc)
From [dbo].[udf-Create-Range-Date](DateAdd(DD,-3*#nDay,#Date),#Date,'DD',1)
Where RetVal not in (Select Date from cteHolidays)
and DatePart(DW,RetVal) between 2 and 6
)
Select #RetVal=RetVal
From cteDays
Where RowNr=#nDay
Return #RetVal
End
It does require a generic helper function which I use to create dynamic date ranges
The helper function
CREATE FUNCTION [dbo].[udf-Create-Range-Date] (#DateFrom datetime,#DateTo datetime,#DatePart varchar(10),#Incr int)
Returns
#ReturnVal Table (RetVal datetime)
As
Begin
With DateTable As (
Select DateFrom = #DateFrom
Union All
Select Case #DatePart
When 'YY' then DateAdd(YY, #Incr, df.dateFrom)
When 'QQ' then DateAdd(QQ, #Incr, df.dateFrom)
When 'MM' then DateAdd(MM, #Incr, df.dateFrom)
When 'WK' then DateAdd(WK, #Incr, df.dateFrom)
When 'DD' then DateAdd(DD, #Incr, df.dateFrom)
When 'HH' then DateAdd(HH, #Incr, df.dateFrom)
When 'MI' then DateAdd(MI, #Incr, df.dateFrom)
When 'SS' then DateAdd(SS, #Incr, df.dateFrom)
End
From DateTable DF
Where DF.DateFrom < #DateTo
)
Insert into #ReturnVal(RetVal) Select DateFrom From DateTable option (maxrecursion 32767)
Return
End
-- Syntax Select * from [dbo].[udf-Create-Range-Date]('2016-10-01','2020-10-01','YY',1)
-- Syntax Select * from [dbo].[udf-Create-Range-Date]('2016-10-01','2020-10-01','DD',1)
-- Syntax Select * from [dbo].[udf-Create-Range-Date]('2016-10-01','2016-10-31','MI',15)
-- Syntax Select * from [dbo].[udf-Create-Range-Date]('2016-10-01','2016-10-02','SS',1)
EDIT
To validate the example listed above
Date Weekday BusDayNr
2016-01-20 Wednesday 1 << Selected Date
2016-01-19 Tuesday 2
2016-01-18 Monday - << Holiday Martin Luther King, Jr
2016-01-17 Sunday -
2016-01-16 Saturday -
2016-01-15 Friday 3 << Date Returned
2016-01-14 Thursday
2016-01-13 Wednesday
2016-01-12 Tuesday
2016-01-11 Monday
here is another approach:
if you putt weekends and holidays into 1 table something like this should work:
declare #holidays_weekends table (
dates date)
insert into #holidays_weekends
values('2016-09-23'),
('2016-09-24'),
('2016-09-25')
declare
#date date = getdate()
select dateadd("dd", -3 -(
select count(*)
from #holidays_weekends
where dates in(
dateadd("dd",-1,#date),
dateadd("dd",-2,#date),
dateadd("dd",-3,#date),
dateadd("dd",-4,#date),
dateadd("dd",-5,#date),
dateadd("dd",-6,#date),
dateadd("dd",-7,#date))) ,#date) as '3rd_day'

SQL- How to split two dates into yearly format

If I have two dates for example:
#StartDate = '2009/01/01'
#EndDate = '2015/02/05'
is there a way for it to display in this format:
StartDate EndDate
2009/01/01 2010/01/01
2010/01/01 2011/01/01
2011/01/01 2012/01/01
2012/01/01 2013/01/01
2013/01/01 2014/01/01
2014/01/01 2015/01/01
2015/01/01 2015/02/05 <--- most importantly end using the end date
is this possible? because I have seen a number of CTE code snippets but they only split upto 01/01/2015 and not continue to the end of period 05/02/2015? Is there any possible chance for it include the remainder of the final period - 2015/01/01 - 2015/02/05 even though its not a year?
Here is a way:
with nums as (
select 0 as n
union all
select 1 + n
from nums
where n < 100
)
select DATEADD(year, n, #StartDate) as StartDate,
(case when DATEADD(year, n+1, #StartDate) >= #EndDate then #EndDate
else DATEADD(year, n+1, #StartDate)
end) as EndDate
from nums
where dateadd(year, nums.n, #StartDate) < #EndDate
If your periods are really long, you might need to expand nums beyond 100 values.
DECLARE #StartDate DATETIME = '2009/01/01'
DECLARE #EndDate DATETIME = '2015/02/05'
;WITH Dates AS
(
SELECT
StartDate = #StartDate,
EndDate = DATEADD(YEAR, 1, #StartDate)
UNION ALL
SELECT
StartDate = DATEADD(YEAR, 1, StartDate),
EndDate = CASE
WHEN DATEADD(YEAR, 1, EndDate) > #EndDate
AND DATEADD(YEAR, -1, EndDate) < DATEADD(YEAR, -1, #EndDate) THEN #EndDate
ELSE DATEADD(YEAR, 1, EndDate)
END
FROM Dates
WHERE EndDate < #EndDate
)
SELECT * FROM dates
OPTION (MAXRECURSION 0)
demo

Returns date range by quarter?

I am looking for a query that can returns a series of date range that is one quarter long.
For example, if the input is 2/1/2013 and 3/31/2014, then the output would look like:
start end
2/1/2013 4/30/2013
5/1/2013 7/31/2013
8/1/2013 10/31/2013
11/1/2013 1/31/2014
2/1/2014 3/31/2014
Notice that the last quarter is only 2 months long. Thanks for help in advance.
Just want to add that this is what I did after I did a bit of googling. I was thinking of some more efficient way but I think this is sufficient for my purpose. The first part is to populate a date table, the second part to calculate the quarter range.
DECLARE #StartDate SMALLDATETIME
DECLARE #EndDate SMALLDATETIME
SET #StartDate = '1/1/2011'
SET #EndDate = '12/31/2011'
-- creates a date table, not needed if there is one already
DECLARE #date TABLE ( [date] SMALLDATETIME )
DECLARE #offset INT
SET #offset = 0
WHILE ( #offset < DATEDIFF(dd, #StartDate, DATEADD(dd, 1, #EndDate)) )
BEGIN
INSERT INTO #date ( [date] )
VALUES ( DATEADD(dd, #offset, #StartDate) )
SELECT #offset = #offset + 1
END ;
WITH dateCTE
AS ( SELECT ROW_NUMBER() OVER ( ORDER BY [date] ASC ) AS qID ,
[date] AS qStart ,
CASE WHEN DATEADD(dd, -1, DATEADD(q, 1, [date])) > #EndDate
THEN #EndDate
ELSE DATEADD(dd, -1, DATEADD(q, 1, [date]))
END AS qEnd
FROM #date
WHERE [date] = #StartDate
OR ( DATEDIFF(mm, #StartDate, [date]) % 3 = 0
AND DATEPART(dd, [date]) = DATEPART(dd,
#StartDate)
)
)

Return number of weekdays between 2 dates in T-SQL

I have 2 dates and want to know how many weekdays (mon-fri) there are
e.g.
thu jan 1 20xx
fri jan 2 20xx
sat jan 3 20xx
sun jan 4 20xx
mon jan 5 20xx
jan 1, jan 5 would return 3
(can ignore public holidays)
Try
DateDiff(day, #DtA, #DtB) - 2 * DateDiff(Week, #DtA, #DtB)
this may not work exactly, but you can see the idea. Some slight modification will work.
Assuming the dates can't be more than five and a half years from each other (or use your own tally table instead of master..spt_values):
DECLARE #date1 datetime, #date2 datetime;
SET #date1 = '20110901';
SET #date2 = '20110905';
SELECT COUNT(*)
FROM (
SELECT
Date = DATEADD(day, number, #date1)
FROM master..spt_values
WHERE type = 'P'
AND number between 0 AND DATEDIFF(day, #date1, #date2)
) s
WHERE DATENAME(DW, Date) NOT IN ('Saturday', 'Sunday')
try this:
SET DATEFIRST 1
DECLARE #StartDate datetime
,#EndDate datetime
SELECT #StartDate='6/21/2011'
,#EndDate='6/28/2011'
;with AllDates AS
(
SELECT #StartDate AS DateOf, datepart(weekday,getdate()) AS WeekDayNumber
UNION ALL
SELECT DateOf+1, datepart(weekday,DateOf+1)
FROM AllDates
WHERE DateOf<#EndDate
)
SELECT COUNT(*) AS WeekDayCount FROM AllDates WHERE WeekDayNumber<=5
OUTPUT:
WeekDayCount
------------
6
(1 row(s) affected)
If you have a holiday table, you can join it in and remove those as well.
EDIT based on #Ross Watson comment:
SET DATEFIRST 1
DECLARE #StartDate datetime
,#EndDate datetime
SELECT #StartDate='6/21/2011'
,#EndDate='6/28/2011'
;with AllDates AS
(
SELECT #StartDate AS DateOf, datepart(weekday,getdate()) AS WeekDayNumber
UNION ALL
SELECT DateOf+1, (WeekDayNumber+1) % 7
FROM AllDates
WHERE DateOf<#EndDate
)
SELECT COUNT(*) AS WeekDayCount FROM AllDates WHERE WeekDayNumber>0 AND WeekDayNumber<6
--I don't like using "BETWEEN", ">", ">=", "<", and "<=" are more explicit in defining end points
produces same output as original query.
Try the following:
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SET #StartDate = '2011/06/01'
SET #EndDate = '2011/06/31'
SELECT
(DATEDIFF(dd, #StartDate, #EndDate) + 1) -
(DATEDIFF(wk, #StartDate, #EndDate) * 5) -
(
CASE
WHEN DATENAME(dw, #StartDate) in
('Sunday', 'Tuesday', 'Wednesday', 'Turesday', 'Saturday')
THEN 1
ELSE 0
END
) -
(
CASE
WHEN DATENAME(dw, #EndDate) in
('Sunday', 'Tuesday', 'Wednesday', 'Turesday', 'Saturday')
THEN 1
ELSE 0
END
)
Also see if this is helpful
http://blog.sqlauthority.com/2007/06/08/sql-server-udf-function-to-display-current-week-date-and-day-weekly-calendar/
This approach is limited to ~100 days due to recursion. This works for the date ranges i've tested. Same idea above, removed the math and simplified:
BEGIN
SET DATEFIRST 1
DECLARE #StartDate datetime
,#EndDate datetime
SELECT #StartDate='12/16/2015'
,#EndDate='1/8/2016'
;with AllDates AS
(
SELECT #StartDate AS DateOf
UNION ALL
SELECT DateOf+1
FROM AllDates
WHERE DateOf<#EndDate
)
SELECT COUNT(*) AS WeekDayCount
FROM
AllDates
WHERE
datepart(weekday,DateOf) between 1 AND 5
--SELECT DateOf [date], datepart(weekday,DateOf) [day]
--FROM
-- AllDates
--WHERE
-- datepart(weekday,DateOf) between 1 AND 5
END