SQL Query to find out the non weekends (Monday-Friday) dates of a week from a variable date - sql

I am writing a SQL Query to find out the non weekends dates (i.e. Monday-Friday) of a week from a variable date.
Basically, it should show the dates which are not coming in weekends (Saturday & Sunday) of the week of a given date.
E.g. If the given date is 6th January, 2021. Then the output date should be between 4th-8th January, 2021.
i.e the Week_Start_Date should be 2021-01-04 and Week_End_Date should be 2021-01-08.
I am writing below codes to find out the count of non-weekend days by referring a date and also writing a query to find out the Week_Start_Date & Week_End_Date using the referring date, but unable to combine both of these.
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SET #StartDate = '2021/01/04'
SET #EndDate = '2021/01/08'
SELECT
(DATEDIFF(dd, #StartDate, #EndDate) + 1)
-(DATEDIFF(wk, #StartDate, #EndDate) * 2)
-(CASE WHEN DATENAME(dw, #StartDate) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, #EndDate) = 'Saturday' THEN 1 ELSE 0 END)
SELECT DATEADD(DAY, 2 - DATEPART(WEEKDAY, GETDATE()), CAST(GETDATE() AS DATE)) [Week_Start_Date],
DATEADD(DAY, 8 - DATEPART(WEEKDAY, GETDATE()), CAST(GETDATE() AS DATE)) [Week_End_Date] ;
EDIT:
I am getting the required output using below SQL query :-
SELECT DATEADD(wk,DATEDIFF(wk,0,GETDATE()),0)
SELECT DATEADD(wk,DATEDIFF(wk,0,GETDATE()),4)

I am getting the required output using below SQL query :-
SELECT DATEADD(week,DATEDIFF(week,0,GETDATE()),0)
SELECT DATEADD(week,DATEDIFF(week,0,GETDATE()),4)

Related

Adding count of Holiday days to dates between

DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SET #StartDate = '2012/11/01'
SET #EndDate = '2012/11/05'
SELECT
(DATEDIFF(wk, #StartDate, #EndDate) * 2)
+(CASE WHEN DATENAME(dw, #StartDate) = 'Sunday' THEN 1 ELSE 0 END)
+(CASE WHEN DATENAME(dw, #EndDate) = 'Saturday' THEN 1 ELSE 0 END)
Help to add # of holiday days to this solution? If there are any Holiday days between your start and end dates. Assuming I do have a table called BICalendar with a column isHoliday and value = 1 if that date is a holiday.
To directly answer the question:
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SET #StartDate = '2012/11/01'
SET #EndDate = '2012/11/05'
SELECT (DATEDIFF(wk, #StartDate, #EndDate) * 2) -- To calculate the number of weekend dates between the two input dates
+ (CASE WHEN DATENAME(dw, #StartDate) = 'Sunday' THEN 1 ELSE 0 END) --If the Start date is Sunday, then add one to adjust for non-working days
+ (CASE WHEN DATENAME(dw, #EndDate) = 'Saturday' THEN 1 ELSE 0 END) --If the End date is Saturday, then add one to adjust for non-working days
- (SELECT COUNT(*) --Minus the number of holiday dates between the two dates
FROM BICalendar
WHERE [Date] BETWEEN #STartDate AND #EndDate
AND isHoliday = 1);
I suspect what you're trying to do is calculate the number of working days between the two dates? If so, the below solution should also work:
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SET #StartDate = '2021-05-01'
SET #EndDate = '2021-05-15'
SELECT DATEDIFF(dd, #StartDate, #EndDate) --To calculate the number of days between the two dates
- (DATEDIFF(wk, #StartDate, #EndDate) * 2) -- To remove the weekends between the two dates
+ (CASE WHEN DATENAME(dw, #StartDate) = 'Sunday' THEN 1 ELSE 0 END) --If the Start date is Sunday, then add one to adjust for non-working days
+ (CASE WHEN DATENAME(dw, #EndDate) = 'Saturday' THEN 1 ELSE 0 END) --If the End date is Saturday, then add one to adjust for non-working days
- (SELECT COUNT(*) --Minus the number of holiday dates between the two dates
FROM BICalendar
WHERE [Date] BETWEEN #STartDate AND #EndDate
AND isHoliday = 1);

Calculate total and elapsed number of working days in the month for a given date

I am stuck with a SQL query.
I am trying to calculate two different things in a same query:
Number of business days in a month (this will exclude weekends).
How many days working days have been passed in a month.
Let's say for November (as on 11/9/2018)
no.of business days no. of business days passed
22 7
I tried like this :
WITH cteAllDates AS
(
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SET #StartDate = '10/01/2018'
SET #EndDate = '10/31/2018'
SELECT
(DATEDIFF(dd, #StartDate, #EndDate) + 1)
- (DATEDIFF(wk, #StartDate, #EndDate) * 2)
- (CASE WHEN DATENAME(dw, #StartDate) = 'Sunday' THEN 1 ELSE 0 END)
- (CASE WHEN DATENAME(dw, #EndDate) = 'Saturday' THEN 1 ELSE 0 END) AS x
) AS y
SELECT x
FROM cteAllDates
I would like to create a virtual table so that I can use these fields in my complete query. And if its possible I can do this with GETDATE() and not to declare dates every time.
Since you want to do it in a CTE, and based on the earlier answer you found on SO, here is a version that does it all for the current month without needing to define start and end dates:
EDIT: To create a holiday table
First, create a holiday table. Don't do this every time, make it a persistent table, and keep it filled up with all holidays you need - easter, xmas etc.
create table holidays(holiday date)
insert holidays values ('2018-09-23'),('2018-09-24')
Now the query, including the check for number of holidays between the dates
;with dates as(
select dateadd(d,-day(getdate())+1,convert(date,getdate())) as startofmonth,
dateadd(d,-1,dateadd(m,1,dateadd(d,-day(getdate())+1,convert(date,getdate())))) as endofmonth,
convert(date,getdate()) as today
)
,holidaycount as (
select count(*) as holidaysinmonth,
sum(case when holiday<=today then 1 else 0 end) as holidaystodate
from dates
join holidays on holiday between startofmonth and endofmonth
)
,daycounts as(
select dates.*,
(DATEDIFF(dd, startofmonth, endofmonth) + 1)
-(DATEDIFF(wk, startofmonth, endofmonth) * 2)
-(CASE WHEN DATENAME(dw, startofmonth) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, endofmonth) = 'Saturday' THEN 1 ELSE 0 END)
-isnull(holidaysinmonth,0) as wkdaysinmonth,
(DATEDIFF(dd, startofmonth, today) + 1)
-(DATEDIFF(wk, startofmonth, today) * 2)
-(CASE WHEN DATENAME(dw, startofmonth) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, today) = 'Saturday' THEN 1 ELSE 0 END)
-isnull(holidaystodate,0) as wkdaystodate
from dates
cross join holidaycount
)
select * from daycounts
EDIT: If you cant create a temp table, add this as an additional CTE before the holidaycount one like so:
,holidays as (
select holiday from (values ('2018-11-23'),('2018-11-24')) t(holiday)
)
,holidaycount as (
You can try to use cte recursive make a calendar table, then use condition aggregate function to get your result.
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SET #StartDate = DATEADD(DAY, 1, EOMONTH(GETDATE(), -1));
SET #EndDate = DATEADD(DAY, 1, EOMONTH(GETDATE()));
;WITH CTE AS (
select #StartDate startdt,#EndDate enddt
UNION ALL
SELECT DATEADD (day ,1 , startdt) , #EndDate
FROM CTE
WHERE DATEADD (day,1,startdt) <= #EndDate
)
select SUM(CASE WHEN DATENAME(dw, startdt) NOT IN ('Sunday','Saturday') THEN 1 END) 'no.of business days',
SUM(CASE WHEN DATENAME(dw, startdt) NOT IN ('Sunday','Saturday') AND GETDATE() >= startdt THEN 1 END) 'no. of business days passed'
FROM CTE
sqlfiddle
Result
no.of business days no. of business days passed
22 7

DATEDIFF between two dates, excluding two specific days

In Dubai, the standard weekend runs from Friday - Saturday rather than the traditional Saturday / Sunday.
I am trying to calculate the amount of working days between two given dates, and this is the code I have;
DATEDIFF(dd, DATEADD(hour, DATEDIFF(hh, getdate(), getutcdate()), #StartDate),
DATEADD(hour, DATEDIFF(hh, getdate(), getutcdate()), #EndDate)+1)
-
(
(DATEDIFF(wk, #StartDate, #EndDate) * 2
+(CASE WHEN DATENAME(dw, #StartDate) = 'Saturday' then 1 else 0 end)
+(CASE WHEN DATENAME(dw, #EndDate) = 'Friday' then 1 else 0 end)
))
)
However it is calculating the wrong amount of days. For example;
When the start date is 02-03-2016 and the end date is 02-06-2016 the returns as 4, but it should be 2.
When the start date is 02-03-2016 and the end date is 02-07-2016 the result is 3 which is correct.
The code below calculates the working days from your examples correctly. You can wrap it in a scalar function if you want.
declare #from date, #to date;
set #from = '2016-02-03';
set #to = '2016-02-06';
with dates(date)
as
(
select #from
union all
select dateadd(dd, 1, [date])
from dates
where [date] < #to
)
select count(*) [working days]
from (
select date, case when datename(dw, [date]) in ('Friday', 'Saturday') then 0 else 1 end as working
from dates) as dt
where dt.working = 1

how do I get the EXACT number of months between two dates?

DATEDIFF(MONTH, '1/1/2014', '12/31/2014') + 1
This will give me 12.
What if I do this:
DATEDIFF(MONTH, '1/1/2014', '12/30/2014') + 1
It should give me 11 point something. How do I go about getting the exact number of months between these two dates? This needs to work for any combination of dates (any month of the year for any year).
You could do the calculation yourself in the following way:
DECLARE #startdate date = '1/1/2014'
DECLARE #enddate date = '12/30/2014'
DECLARE #startday int = DATEPART(DAY, #startdate)
DECLARE #endday int = DATEPART(DAY, #enddate)
DECLARE #startdateBase date = DATEADD(DAY, 1 - #startday, #startdate)
DECLARE #enddateBase date = DATEADD(DAY, 1 - #endday, #enddate)
DECLARE #deciMonthDiff float = CAST(DATEDIFF(MONTH, #startdate, #enddate) AS float) -
(#startday - 1.0) / DATEDIFF(DAY, #startdateBase, DATEADD(MONTH, 1, #startdateBase)) +
(#endday - 1.0) / DATEDIFF(DAY, #enddateBase, DATEADD(MONTH, 1, #enddateBase))
SELECT #deciMonthDiff
This calculates the #deciMonthDiff to be 11.935483870967.
Of course you can "inline" this as much as you want in order to avoid all the middle declarations.
The idea is to calculate the total month diff, then subtract the relative part of the first & last month depending on the actual day.
DATEDIFF with the MONTH option only returns an integer value. Using days or years would give you a rough "guesstimate" but still not exactly right (different number of days in a month/year so you can't just divide the days difference by 30).
If you want exact you would need to write your own function to walk through the months from start until end and account for how many days are in each month and get a percentage/factor of that month covered.
DECLARE #startdate date = '1/1/2014'
DECLARE #enddate date = '12/30/2014'
select case when DATEPART(DAY, #startdate) <= DATEPART(DAY, #enddate)
then datediff(month, #startdate, #enddate)
else datediff(month, #startdate, #enddate) -1
end
from Chris The Jedi of T-SQL

count week days in sql by datediff [duplicate]

This question already has answers here:
Count work days between two dates
(24 answers)
Calculating days to excluding weekends (Monday to Friday) in SQL Server
(6 answers)
Closed 7 years ago.
I'm looking for a way to calculate the days between two dates, but on weekdays. Here is the formula, but it counts weekend days.
DATEDIFF(DAY,STARTDATE,ENDDATE)
SELECT DATEDIFF(DAY,'2015/06/01' , '2015/06/30')
Result of above query of datediff is 29 days which are weekend days. but i need week days that should be 21 by removing Saturday and Sunday(8 days).
Any suggestions?
Put it in the WHERE clause
SELECT DATEDIFF(DAY,'2015/06/01' , '2015/06/30')
FROM yourtable
WHERE DATENAME(dw, StartDate) != 'Saturday'
AND DATENAME(dw, StartDate) != 'Sunday'
Or all in a SELECT statement
SELECT (DATEDIFF(dd, StartDate, EndDate) + 1)-(DATEDIFF(wk, StartDate, EndDate) * 2)-(CASE WHEN DATENAME(dw, StartDate) = 'Sunday' THEN 1 ELSE 0 END)-(CASE WHEN DATENAME(dw, EndDate) = 'Saturday' THEN 1 ELSE 0 END)
This returns 22:
DECLARE #StartDate AS DATE = '20150601'
DECLARE #EndDate AS DATE = '20150630'
SELECT
(DATEDIFF(DAY, #StartDate, #EndDate))
-(DATEDIFF(WEEK, #StartDate, #EndDate) * 2)
-(CASE WHEN DATENAME(dw, #StartDate) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, #EndDate) = 'Saturday' THEN 1 ELSE 0 END)
Read this article by Jeff Moden for more information.
Explanation:
First, (DATEDIFF(DAY, #StartDate, #EndDate)) will return the difference in number of days. In this case, it'll be 29. Now, depending on your interpretation of whole days, you may want to add 1 day to its result.
Next,(DATEDIFF(WEEK, #StartDate, #EndDate) * 2):
To quote the article:
DATEDIFF for the WEEK datepart doesn't actually calculate weeks, it
calculates the number of times a date range contains dates that
represent pairs of Saturdays and Sundays. To think of it in more
simple terms, it only counts WHOLE WEEKENDS!
So, to exclude the weekends, you must subtract twice the result of this from the first DATEDIFF. Which now will be: 29 - (2 *4) = 21.
Finally, this:
-(CASE WHEN DATENAME(dw, #StartDate) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, #EndDate) = 'Saturday' THEN 1 ELSE 0 END)
removes the partial weeks, which only happens when then #StartDate occurs on a Sunday and the #EndDate occurs on a Saturday.
You can try recursive cte:
WITH cte
AS ( SELECT CAST('2015/06/01' AS DATE) AS dt ,
DATEPART(WEEKDAY, '2015/06/01') AS wd
UNION ALL
SELECT DATEADD(d, 1, dt) AS dt ,
DATEPART(WEEKDAY, DATEADD(d, 1, dt))
FROM cte
WHERE dt < '2015/06/30'
)
SELECT COUNT(*)
FROM cte
WHERE wd NOT IN ( 7, 1 )
Result is 22.
You better add some useful calendar table that every database should have. Fill it with some big range and then use it to calculate business days.
I wrote a simple stored procedure - don't know if a SP is what you need but i think you can easily convert it to a function , TVF, whatever:
CREATE PROCEDURE [dbo].[BusinessdaysBetween](#DateFrom DATE, #DateTo DATE, #days INT OUTPUT)
AS
BEGIN
DECLARE #datefirst INT = ##DATEFIRST
SET DATEFIRST 1 --so week starts on monday
SET #days = 0
IF #DateFrom > #DateTo
RETURN NULL
IF #DateFrom = #DateTo
RETURN #days
WHILE #DateFrom <= #DateTo
BEGIN
IF DATEPART(WEEKDAY,#DateFrom) NOT IN (6,7) --Saturday or Sunday
BEGIN
SET #days = #days + 1
END
SET #DateFrom = DATEADD(DAY,1,#DateFrom)
END
SET DATEFIRST #datefirst --restore original setup
END
GO
in my example i call i it like this:
DECLARE #days INT = 0
DECLARE #datefrom DATETIME = GETDATE()
DECLARE #dateto DATETIME = DATEADD(DAY,25,GETDATE())
EXEC dbo.BusinessdaysBetween #datefrom, #dateto, #days OUTPUT
SELECT #days