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

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'

Related

Get the remaining days between two dates

I am trying to calculate the remaining year, month and days between 2 dates using the method below, but I cannot get the remaining dates right. Is there a function or different method I should try?
Select (DATEDIFF(m, '1965-10-17', '2021-04-07')) /12 as AgeYr ,
(DATEDIFF(m, '1965-10-17', '2021-04-07')) %12 as AgeMth,
(DATEDIFF(d, '1965-10-17', '2021-04-07')) %30 as AgeDay;
This is the result of my code
AgeYr
AgeMth
AgeDay
55
6
10
but the result should be
AgeYr
AgeMth
AgeDay
55
6
21
DECLARE #date datetime, #startdate datetime, #enddate datetime, #years int, #months int, #days int
SELECT #startdate = '1965-10-17'
SELECT #enddate = '2021-04-07'
SELECT #date = #startdate
SELECT #years = DATEDIFF(yy, #startdate, #enddate) - CASE WHEN (MONTH(#date) > MONTH(#enddate)) OR (MONTH(#date) = MONTH(#enddate) AND DAY(#date) > DAY(#enddate)) THEN 1 ELSE 0 END
SELECT #startdate = DATEADD(yy, #years, #startdate)
SELECT #months = DATEDIFF(m, #startdate, #enddate) - CASE WHEN DAY(#date) > DAY(#enddate) THEN 1 ELSE 0 END
SELECT #startdate = DATEADD(m, #months, #startdate)
SELECT #days = DATEDIFF(d, #startdate, #enddate)
SELECT #years years, #months months, #days days
Output:
AgeYr
AgeMth
AgeDay
55
5
21
db<>fiddle here
You can also do it with common table expression:
;with years as
(SELECT (DATEDIFF(yy, '1965-10-17', '2021-04-07') - CASE WHEN (MONTH('1965-10-17') > MONTH('2021-04-07')) OR (MONTH('1965-10-17') = MONTH('2021-04-07') AND DAY('1965-10-17') > DAY('2021-04-07')) THEN 1 ELSE 0 END)AgeYr,
'1965-10-17' startdate)
,months as
(SELECT (DATEDIFF(m, DATEADD(yy, years.AgeYr, '1965-10-17'), '2021-04-07') - CASE WHEN DAY('1965-10-17') > DAY('2021-04-07') THEN 1 ELSE 0 END)AgeMth, DATEADD(yy, years.AgeYr, '1965-10-17') temp
from years)
,days as
(select DATEDIFF(d, DATEADD(m, months.AgeMth, temp), '2021-04-07')AgeDay from months)
select years.AgeYr,months.AgeMth,days.AgeDay from years,months,days
Output:
AgeYr
AgeMth
AgeDay
55
5
21
db<>fiddle here

SQL Set Time to Midnight in function

I have This function
ALTER FUNCTION [General].[GetWeekEnding]
(
#Date DATETIME
)
RETURNS DATETIME
AS
BEGIN
-- Return the result of the function
RETURN (DATEADD(day, -1 - (DATEPART(dw, #Date) + ##DATEFIRST - 2) % 7, #Date) + 7)
END
I need to also set the time to 00:00:00.000 as well as finding the week ending of a provided date any thoughts?
I use a UDF for adding time components to dates e.g.
CREATE FUNCTION [dbo].[DateTimeAdd]
(
#datepart date,
#timepart time
)
RETURNS datetime2
AS
BEGIN
RETURN DATEADD(dd, DATEDIFF(dd, 0, #datepart), CAST(#timepart AS datetime2));
END
Then in your case you can use it like this:
SELECT dbo.DateTimeAdd(DATEADD (D, -1 * DatePart (DW, GetDate()) + 7, GetDate()), DATEADD(hh, 0, CAST(DATEADD(DAY, DATEDIFF(DAY, -1, GETDATE()), -1) AS TIME)))
Since SQL Server 2005, the best way to remove time is to cast to the date data type:
select cast(#Date as date)
I would suggest that you have your function return a date instead of a datetime.
To get to the week ending, you need a lookup table to map days of the week to integers. You can do this with datepart. However, that is subject to system settings. Here is a function:
Create FUNCTION [GetWeekEnding] (
#Date DATETIME,
#WeekEndingDOW varchar(10)
)
RETURNS DATE
AS
BEGIN
-- Return the result of the function
declare #newdate datetime;
with lookup as (
select 'Sunday' as dow, 0 as daynum union all
select 'Monday' as dow, 1 as daynum union all
select 'Tuesday' as dow, 2 as daynum union all
select 'Wednesday' as dow, 3 as daynum union all
select 'Thursday' as dow, 4 as daynum union all
select 'Friday' as dow, 5 as daynum union all
select 'Saturday' as dow, 6 as daynum
)
select #newdate = #Date - (select daynum from lookup where datename(dw, #date) = dow) + (select daynum from lookup where #WeekEndingDOW = dow);
select #newdate = (case when #newdate < #date then #newdate + 7 else #newdate end)
RETURN cast(#newdate as date)
END;
Note that this function does use datetime internally. For some reason that I cannot fathom, you can say "#date + 1" to mean "add one day to the date value" when #date is a datetime. However, this does not work when #date is a date. (This is also true of the dateadd function.)

Get number of weekdays (Sundays, Mondays, Tuesdays) between two dates SQL

So, I'd like to, for a Start Date and End Date, determine how many particular days of the week occur between these two dates.
So how many mondays, tuesdays, etc
I know I can do this with a loop between the Start and End Date and check each day, but the difference could possibly be a great number of days. I'd prefer something that didn't require a loop. Any ideas? (Must be supported in SQL Server 2005+)
Given what I think you're trying to get, this should do it:
SET DATEFIRST 1
DECLARE
#start_date DATETIME,
#end_date DATETIME
SET #start_date = '2011-07-11'
SET #end_date = '2011-07-22'
;WITH Days_Of_The_Week AS (
SELECT 1 AS day_number, 'Monday' AS day_name UNION ALL
SELECT 2 AS day_number, 'Tuesday' AS day_name UNION ALL
SELECT 3 AS day_number, 'Wednesday' AS day_name UNION ALL
SELECT 4 AS day_number, 'Thursday' AS day_name UNION ALL
SELECT 5 AS day_number, 'Friday' AS day_name UNION ALL
SELECT 6 AS day_number, 'Saturday' AS day_name UNION ALL
SELECT 7 AS day_number, 'Sunday' AS day_name
)
SELECT
day_name,
1 + DATEDIFF(wk, #start_date, #end_date) -
CASE WHEN DATEPART(weekday, #start_date) > day_number THEN 1 ELSE 0 END -
CASE WHEN DATEPART(weekday, #end_date) < day_number THEN 1 ELSE 0 END
FROM
Days_Of_The_Week
I'm not sure what the OP is after, this will give a count per weeks day:
SET DATEFIRST 1
DECLARE #StartDate datetime
,#EndDate datetime
SELECT #StartDate='7/13/2011'
,#EndDate='7/28/2011'
;with AllDates AS
(
SELECT #StartDate AS DateOf, datename(weekday,#StartDate) AS WeekDayName, datepart(weekday,#StartDate) AS WeekDayNumber
UNION ALL
SELECT DateOf+1, datename(weekday,DateOf+1), datepart(weekday,DateOf+1)
FROM AllDates
WHERE DateOf<#EndDate
)
SELECT COUNT(*) CountOf,WeekDayName FROM AllDates GROUP BY WeekDayName,WeekDayNumber ORDER BY WeekDayNumber
OUTPUT:
CountOf WeekDayName
----------- ------------------------------
2 Monday
2 Tuesday
3 Wednesday
3 Thursday
2 Friday
2 Saturday
2 Sunday
(7 row(s) affected)
this will give a count of Monday to Friday days:
SET DATEFIRST 1
DECLARE #StartDate datetime
,#EndDate datetime
SELECT #StartDate='7/13/2011'
,#EndDate='7/28/2011'
;with AllDates AS
(
SELECT #StartDate AS DateOf, datepart(weekday,#StartDate) 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
------------
12
(1 row(s) affected)
If you have a holiday table, you can join it in and remove those as well. Here is a slightly different version that may preform better:
SET DATEFIRST 1
DECLARE #StartDate datetime
,#EndDate datetime
SELECT #StartDate='7/13/2011'
,#EndDate='7/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.
This assume standard settings but cans be adapted
DECLARE #StartDate datetime, #EndDate datetime
SELECT #StartDate='20110601', #EndDate='20110630'
;WITH AllDates AS
(
SELECT #StartDate AS DateOf, datepart(weekday, #StartDate) AS WeekDayNumber
UNION ALL
SELECT DateOf+1, datepart(weekday, DateOf+1)
FROM AllDates
WHERE DateOf < #EndDate
)
SELECT SUM(CASE WHEN WeekDayNumber BETWEEN 2 AND 6 THEN 1 ELSE 0 END) AS WeekDayCount
FROM AllDates
OPTION (MAXRECURSION 0)
This should be valid for SQL Server, and should be internationlization safe (note: I do not have a server to test this against).
SELECT datediff(day, #start, #end) - datediff(week, #start, #end) * 2
- CASE WHEN datepart(weekday, #start)
IN (datepart(weekday, '1970-01-03'),
datepart(weekday, '1970-01-04'))
THEN 1
ELSE 0 END,
- CASE WHEN datepart(weekday, #end)
IN (datepart(weekday, '1970-01-03'),
datepart(weekday, '1970-01-04'))
THEN 1
ELSE 0 END
Give that a whirl.
Given the clarification, this should get the number of each of the days.
Uses no recursion, and should be completely international-safe. You will have to adjust start/end date parameters for inclusion/exclusion as necessary (The DB2 version I was using to check this excluded the start date, but included the end date, for example).
WITH dayOfWeek (name, dayNumber) as (VALUES(dayname(weekday, '1970-01-01'), daypart(weekday, '1970-01-01')),
(dayname(weekday, '1970-01-02'), daypart(weekday, '1970-01-02')),
(dayname(weekday, '1970-01-03'), daypart(weekday, '1970-01-03')),
(dayname(weekday, '1970-01-04'), daypart(weekday, '1970-01-04')),
(dayname(weekday, '1970-01-05'), daypart(weekday, '1970-01-05')),
(dayname(weekday, '1970-01-06'), daypart(weekday, '1970-01-06')),
(dayname(weekday, '1970-01-07'), daypart(weekday, '1970-01-07')))
SELECT name, dayNumber, datediff(weeks, #start, #end)
+ CASE WHEN datepart(weekday, #end) >= dayNumber THEN 1 ELSE 0 END
- CASE WHEN datepart(weekday, #start) >= dayNumber THEN 1 ELSE 0 END
FROM dayOfWeek
Does that help any?
you can use DATEDIFF and DATEPART functions + some basic math to get the desired result without looping.
I would have simply added as a comment to the marked answer, but do not have enough "reputation". Instead of hardcoding the day_number which is dependent on datefirst, you could set it by discovering the weekday for each day of the week:
;WITH Days_Of_The_Week AS (
SELECT DATEPART(dw, '2007-01-01') AS day_number, 'Monday' AS day_name UNION ALL -- 2007-01-01 is a known Monday
SELECT DATEPART(dw, '2007-01-02') AS day_number, 'Tuesday' AS day_name UNION ALL
SELECT DATEPART(dw, '2007-01-03') AS day_number, 'Wednesday' AS day_name UNION ALL
SELECT DATEPART(dw, '2007-01-04') AS day_number, 'Thursday' AS day_name UNION ALL
SELECT DATEPART(dw, '2007-01-05') AS day_number, 'Friday' AS day_name UNION ALL
SELECT DATEPART(dw, '2007-01-06') AS day_number, 'Saturday' AS day_name UNION ALL
SELECT DATEPART(dw, '2007-01-07') AS day_number, 'Sunday' AS day_name
)
#start_date date = '2017-08-11',
#end_date date = '2017-08-27',
#weekday int = 7,
#count int output
As
Begin
Declare #i int = 0
set #count = 0
while(#i <= (select Datediff(Day, #start_date, #end_date)))
begin
if(Dateadd(Day, #i, #start_date) > #end_date)
break
if(Datepart(weekday, Dateadd(Day, #i, #start_date)) = #weekday)
set #count += 1
set #i += 1
end
select #count

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

Find last sunday

How will you find last sunday of a month in sql 2000?
SELECT
DATEADD(day,DATEDIFF(day,'19000107',DATEADD(month,DATEDIFF(MONTH,0,GETDATE() /*YourValuehere*/),30))/7*7,'19000107')
Edit: A correct, final, working answer from my colleague.
select dateadd(day,1-datepart(dw, getdate()), getdate())
An alternative approach, borrowed from data warehousing practice. Create a date-dimension table and pre-load it for 10 years, or so.
TABLE dimDate (DateKey, FullDate, Day, Month, Year, DayOfWeek,
DayInEpoch, MonthName, LastDayInMonthIndicator, many more..)
The easiest way to fill-in the dimDate is to spend an afternoon with Excel and then import to DB from there. A half-decent dimDate table has 50+ columns -- anything you ever wanted to know about a date.
With this in place, the question becomes something like:
SELECT max(FullDate)
FROM dimDate
WHERE DayOfWeek = 'Sunday'
AND Month = 11
AND Year = 2009;
Essentially, all date related queries become simpler.
Next sunday in SQL, regardless which day is first day of week: returns 2011-01-02 23:59:59.000 on 22-dec-2010:
select DateADD(ss, -1, DATEADD(week, DATEDIFF(week, 0, getdate()), 14))
I find some of these solutions hard to understand so here's my version with variables to explain the steps.
ALTER FUNCTION dbo.fn_LastSundayInMonth
(
#StartDate DATETIME
,#RequiredDayOfWeek INT /* 1= Sunday */
)
RETURNS DATETIME
AS
/*
A detailed step by step way to get the answer...
SELECT dbo.fn_LastSundayInMonth(getdate()-31,1)
SELECT dbo.fn_LastSundayInMonth(getdate()-31,2)
SELECT dbo.fn_LastSundayInMonth(getdate()-31,3)
SELECT dbo.fn_LastSundayInMonth(getdate()-31,4)
SELECT dbo.fn_LastSundayInMonth(getdate()-31,5)
SELECT dbo.fn_LastSundayInMonth(getdate()-31,6)
SELECT dbo.fn_LastSundayInMonth(getdate()-31,7)
*/
BEGIN
DECLARE #MonthsSince1900 INTEGER
DECLARE #NextMonth INTEGER
DECLARE #DaysToSubtract INTEGER
DECLARE #FirstDayOfNextMonth DATETIME
DECLARE #LastDayOfMonthDayOfWeek INTEGER
DECLARE #LastDayOfMonth DATETIME
DECLARE #ReturnValue DATETIME
SET #MonthsSince1900=DateDiff(month, 0, #StartDate)
SET #NextMonth=#MonthsSince1900+1
SET #FirstDayOfNextMonth = DateAdd(month,#NextMonth, 0)
SET #LastDayOfMonth = DateAdd(day, -1, #FirstDayOfNextMonth)
SET #ReturnValue = #LastDayOfMonth
WHILE DATEPART(dw, #ReturnValue) <> #RequiredDayOfWeek
BEGIN
SET #ReturnValue = DATEADD(DAY,-1, #ReturnValue)
END
RETURN #ReturnValue
END
DECLARE #LastDateOfMonth smalldatetime
SELECT #LastDateOfMonth = DATEADD(month, DATEDIFF(month, -1, GETDATE()), 0) -1
Select DATEADD(dd,-( CASE WHEN DATEPART(weekday,#LastDateOfMonth) = 1 THEN 0 ELSE DATEPART(weekday,#LastDateOfMonth) - 1 END ),#LastDateOfMonth)
Holy cow, this is ugly, but here goes:
DECLARE #dtDate DATETIME
SET #dtDate = '2009-11-05'
SELECT DATEADD(dd, -1*(DATEPART(dw, DateAdd(day, -1, DateAdd(month, DateDiff(month, 0, #dtDate)+1, 0)))-1),
DateAdd(day, -1, DateAdd(month, DateDiff(month, 0, #dtDate)+1, 0)))
First built a tally table.
http://www.sqlservercentral.com/articles/T-SQL/62867/
then get what you want..
http://www.sqlservercentral.com/Forums/Topic515226-1291-1.aspx
DECLARE #DateStart DATETIME,
#DateEnd DATETIME
SELECT #DateStart = '20080131',
#DateEnd = '20101201'
SELECT DATEADD(wk,DATEDIFF(wk,6,DATEADD(mm,DATEDIFF(mm,-1,DATEADD(mm,t.N-1,#DateStart)),-1)),6)
FROM dbo.Tally t
WHERE t.N <= DATEDIFF(mm,#DateStart,#DateEnd)
Here's the correct way, accounting for ##DATEFIRST
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[fu_dtLastSundayInMonth]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
BEGIN
EXECUTE(N'CREATE FUNCTION [dbo].[fu_dtLastSundayInMonth]() RETURNS int BEGIN RETURN 0 END ')
END
GO
/*
SET DATEFIRST 3; -- Monday
WITH CTE AS (
SELECT 1 AS i, CAST('20190101' AS datetime) AS mydate
UNION ALL
SELECT i+1 AS i, DATEADD(month, 1, CTE.mydate) AS mydate
FROM CTE WHERE i < 100
)
SELECT -666 AS i, dbo.fu_dtLastSundayInMonth('17530101') AS lastSundayInMonth, dbo.fu_dtLastSundayInMonth('17530101') AS Control
UNION ALL
SELECT -666 AS i, dbo.fu_dtLastSundayInMonth('99991231') AS lastSundayInMonth, dbo.fu_dtLastSundayInMonth('99991231') AS Control
UNION ALL
SELECT
mydate
,dbo.fu_dtLastSundayInMonth(mydate) AS lastSundayInMonth
,dbo.fu_dtLastSundayInMonth(mydate) AS lastSundayInMonth
,DATEADD(day,DATEDIFF(day,'19000107', DATEADD(MONTH, DATEDIFF(MONTH, 0, mydate, 30))/7*7,'19000107') AS Control
FROM CTE
*/
-- =====================================================================
-- Description: Return date of last sunday in month
-- of the same year and month as #in_DateTime
-- =====================================================================
ALTER FUNCTION [dbo].[fu_dtLastSundayInMonth](#in_DateTime datetime )
RETURNS DateTime
AS
BEGIN
-- Abrunden des Eingabedatums auf 00:00:00 Uhr
DECLARE #dtReturnValue AS DateTime
-- 26.12.9999 SO
IF #in_DateTime >= CAST('99991201' AS datetime)
RETURN CAST('99991226' AS datetime);
-- #dtReturnValue is now last day of month
SET #dtReturnValue = DATEADD
(
DAY
,-1
,DATEADD
(
MONTH
,1
,CAST(CAST(YEAR(#in_DateTime) AS varchar(4)) + RIGHT('00' + CAST(MONTH(#in_DateTime) AS varchar(2)), 2) + '01' AS datetime)
)
)
;
-- SET DATEFIRST 1 -- Monday - Super easy !
-- SET DATEFIRST != 1 - PHUK THIS !
SET #dtReturnValue = DATEADD
(
day
,
-
(
(
-- DATEPART(WEEKDAY, #lastDayofMonth) -- with SET DATEFIRST 1
DATEPART(WEEKDAY, #dtReturnValue) + ##DATEFIRST - 2 % 7 + 1
)
%7
)
, #dtReturnValue
);
RETURN #dtReturnValue;
END
GO
select next_day(last_day(sysdate)-7, 'Sunday') from dual