I have 2 Sets for Date Ranges :
#StartDate and #EndDate : Being passed to a stored procedure
ProjectStartDate and ProjectEndDate : In the Database
The goal is to get the number of working days (public holidays are ignored) that ProjectStartDate and ProjectEndDate fall between #StartDate and #EndDate.
Example 1:
#StartDate = 2016/03/21
#EndDate = 2016/03/25
ProjectStartDate = 2016/03/13
ProjectEndDate = 2016/03/22
So the number of working days I need to work out are where ProjectStartDate and ProjectEndDate fall beween #StartDate and #Endate - in this case, number of working days would be 2(Mon - Fri)
Example 2 :
ProjectStartDate = 2016/03/22
ProjectEndDate = 2016/03/29
So the number of working days I need to work out are where ProjectStartDate and ProjectEndDate fall between #StartDate and #Endate - in this case, number of working days would be 4(Mon - Fri)
Example 3 :
ProjectStartDate = 2016/03/13
ProjectEndDate = 2016/03/29
So the number of working days I need to work out are where ProjectStartDate and ProjectEndDate fall between #StartDate and #Endate - in this case, number of working days would be 5(Mon - Fri)
The code I have to work out the working days is as follows :
(DATEDIFF(dd, ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart), ISNULL(AA.DDAreaActivityActualEnd,AA.DDAreaActivityPlannedEnd)) + 1)
-(DATEDIFF(wk, ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart), ISNULL(AA.DDAreaActivityActualEnd,AA.DDAreaActivityPlannedEnd)) * 2)
-(CASE WHEN DATENAME(dw, ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart)) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart)) = 'Saturday' THEN 1 ELSE 0 END)
I have tried to get the result, but it partially works. I know there is a simpler way of doing this :
CASE WHEN(CASE WHEN #StartDate <= ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart)
THEN (DATEDIFF(dd, ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart), #EndDate) + 1)
-(DATEDIFF(wk, ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart), #EndDate) * 2)
-(CASE WHEN DATENAME(dw, ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart)) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart)) = 'Saturday' THEN 1 ELSE 0 END)
ELSE (DATEDIFF(dd, #StartDate, ISNULL(AA.DDAreaActivityActualEnd,AA.DDAreaActivityPlannedEnd)) + 1)
-(DATEDIFF(wk, #StartDate, ISNULL(AA.DDAreaActivityActualEnd,AA.DDAreaActivityPlannedEnd)) * 2)
-(CASE WHEN DATENAME(dw, #StartDate) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, #StartDate) = 'Saturday' THEN 1 ELSE 0 END) END) >= 5
THEN 5
ELSE (CASE WHEN #StartDate <= ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart)
THEN (DATEDIFF(dd, ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart), #EndDate) + 1)
-(DATEDIFF(wk, ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart), #EndDate) * 2)
-(CASE WHEN DATENAME(dw, ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart)) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart)) = 'Saturday' THEN 1 ELSE 0 END)
ELSE (DATEDIFF(dd, #StartDate, ISNULL(AA.DDAreaActivityActualEnd,AA.DDAreaActivityPlannedEnd)) + 1)
-(DATEDIFF(wk, #StartDate, ISNULL(AA.DDAreaActivityActualEnd,AA.DDAreaActivityPlannedEnd)) * 2)
-(CASE WHEN DATENAME(dw, #StartDate) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, #StartDate) = 'Saturday' THEN 1 ELSE 0 END) END) END AS DaysCalculated
So I ended up finding the answer, it seems. I would prefer to find a cleaner, neater way... but I cant spend more time on this and this works for now. If Anyone has a cleaner, neater option, please let me know. Thanks.
(CASE WHEN ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart) >= #StartDate AND
ISNULL(AA.DDAreaActivityActualEnd,AA.DDAreaActivityPlannedEnd) >= #EndDate
THEN
(DATEDIFF(dd, ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart), #EndDate) + 1)
-(DATEDIFF(wk, ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart), #EndDate) * 2)
-(CASE WHEN DATENAME(dw, ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart)) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart)) = 'Saturday' THEN 1 ELSE 0 END)
ELSE
(CASE WHEN ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart) >= #StartDate AND
ISNULL(AA.DDAreaActivityActualEnd,AA.DDAreaActivityPlannedEnd) <= #EndDate
THEN
(DATEDIFF(dd, ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart), ISNULL(AA.DDAreaActivityActualEnd,AA.DDAreaActivityPlannedEnd)) + 1)
-(DATEDIFF(wk, ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart), ISNULL(AA.DDAreaActivityActualEnd,AA.DDAreaActivityPlannedEnd)) * 2)
-(CASE WHEN DATENAME(dw, ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart)) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart)) = 'Saturday' THEN 1 ELSE 0 END)
ELSE
(CASE WHEN ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart) <= #StartDate AND
ISNULL(AA.DDAreaActivityActualEnd,AA.DDAreaActivityPlannedEnd) <= #EndDate
THEN
(DATEDIFF(dd, #StartDate, ISNULL(AA.DDAreaActivityActualEnd,AA.DDAreaActivityPlannedEnd)) + 1)
-(DATEDIFF(wk, #StartDate, ISNULL(AA.DDAreaActivityActualEnd,AA.DDAreaActivityPlannedEnd)) * 2)
-(CASE WHEN DATENAME(dw, #StartDate) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, #StartDate) = 'Saturday' THEN 1 ELSE 0 END)
ELSE
(CASE WHEN ISNULL(AA.DDAreaActivityActualStart,AA.DDAreaActivityPlannedStart) <= #StartDate AND
ISNULL(AA.DDAreaActivityActualEnd,AA.DDAreaActivityPlannedEnd) >= #EndDate
THEN
(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, #StartDate) = 'Saturday' THEN 1 ELSE 0 END)
END) END) END) END) AS CalculatedDays
How about this
declare #StartDate date = '2016-03-21',
#EndDate date = '2016-03-25'
; with tbl (ProjectStartDate , ProjectEndDate) as
(
select '2016-03-13', '2016-03-22' union all
select '2016-03-22', '2016-03-29' union all
select '2016-03-13', '2016-03-29'
)
select *,
[working days] = datediff( day,
case when ProjectStartDate > #StartDate then ProjectStartDate else #StartDate end,
case when ProjectEndDate < #EndDate then ProjectEndDate else #EndDate end
) + 1
from tbl
Related
This is part of a query that is calculating the total revenue for a contract based on the time-frame "This Week" along with the start and end dates of the contract (billed hourly).
SELECT (ChargeRate - PayRate) * 8 *
CASE
WHEN ContractStartDate <= DATEADD(DAY, 1-DATEPART(WEEKDAY, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP)
AND ContractEndDate >= CURRENT_TIMESTAMP
THEN DATEDIFF(DAY, DATEADD(DAY, 1-DATEPART(WEEKDAY, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP), CURRENT_TIMESTAMP)
WHEN ContractEndDate <= CURRENT_TIMESTAMP
AND ContractEndDate >= DATEADD(DAY, 1-DATEPART(WEEKDAY, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP)
AND ContractStartDate <= CURRENT_TIMESTAMP
THEN DATEDIFF(DAY, DATEADD(DAY, 1-DATEPART(WEEKDAY, ContractEndDate), ContractEndDate), ContractEndDate)
WHEN ContractStartDate >= DATEADD(DAY, 1-DATEPART(WEEKDAY, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP)
AND ContractStartDate <= CURRENT_TIMESTAMP
AND ContractStartDate >= CURRENT_TIMESTAMP
THEN DATEDIFF(DAY, ContractStartDate, CURRENT_TIMESTAMP)
WHEN ContractEndDate <= CURRENT_TIMESTAMP
AND ContractEndDate >= DATEADD(DAY, 1-DATEPART(WEEKDAY, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP)
AND ContractStartDate >= CURRENT_TIMESTAMP
THEN DATEDIFF(DAY, ContractStartDate, ContractEndDate)
ELSE DATEDIFF(DAY, DATEADD(DAY, 1-DATEPART(WEEKDAY, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP), CURRENT_TIMESTAMP)
END
What i am struggling with is how i can exclude Saturday and Sunday so the count never goes past 5 and where Sunday is not the start of the week but instead Monday. So the number of days worked would end up being [ Monday = 1, Tuesday = 2 ... Friday = 5, Saturday = 5, Sunday = 5 ] based on what day the query is run.
What happens at the moment is that if these graphs are run on Saturday the calculation uses 6 days, if its run on Sunday the calculation uses 0 days. Every day during the week is correct, this can be seen with the following query:
DECLARE #StartDate DATETIME = '2019-12-1'
DECLARE #StartDate2 DATETIME = '2019-11-30'
SELECT DATEDIFF(DAY, DATEADD(DAY, 1-DATEPART(WEEKDAY, #StartDate), #StartDate), #StartDate)
SELECT DATEDIFF(DAY, DATEADD(DAY, 1-DATEPART(WEEKDAY, #StartDate2), #StartDate2), #StartDate2)
Results should be 0 and 6.
The only solution i can come up with is to nest the case statement and check if the value = 0 or 6 and change it to a 5, like so:
WHEN ContractStartDate <= DATEADD(DAY, 1-DATEPART(WEEKDAY, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP)
AND ContractEndDate >= CURRENT_TIMESTAMP
THEN
CASE
WHEN DATEDIFF(DAY, DATEADD(DAY, 1-DATEPART(WEEKDAY, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP), CURRENT_TIMESTAMP) IN (0,6)
THEN 5
ELSE DATEDIFF(DAY, DATEADD(DAY, 1-DATEPART(WEEKDAY, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP), CURRENT_TIMESTAMP)
END
which works but is a bit messy and i am interested to see if there is a better solution to this.
Here's one way, borrowed from Jeff Moden via SQLServerCentral. (I'd comment this reply, but I don't have enough whacky points :( )
--count weekdays between two dates
DECLARE #StartDate DATETIME = '2019-11-01'
DECLARE #EndDate DATETIME = '2019-11-30'
SELECT (DATEDIFF(DD, #StartDate, #EndDate) + 1) --Total days in period, including weekends
-(DATEDIFF(WK, #StartDate, #EndDate) * 2) --minus number of whole weekends in the period * 2 days
-(CASE WHEN DATENAME(DW, #StartDate) = 'Sunday' THEN 1 ELSE 0 END) --minus 1 if the period starts on a Sunday
-(CASE WHEN DATENAME(DW, #EndDate) = 'Saturday' THEN 1 ELSE 0 END) --minus 1 if the period ends on a Saturday
Of course, there are issues here, such as the use of English day names that won't travel well, but those can be worked around if necessary.
I have variation of this built into a "WeekdayCount" function that comes in very handy!
Way more detail here: https://www.sqlservercentral.com/articles/calculating-work-days
additional solution to already posted by #tim-monfries:
DECLARE #StartDate DATETIME = '2019-11-01';
DECLARE #EndDate DATETIME = '2019-11-30';
WITH cte AS
(
SELECT #StartDate AS SomeDate
UNION ALL
SELECT SomeDate+1 FROM cte WHERE SomeDate < #EndDate
)
SELECT COUNT(*)
FROM cte
WHERE DATENAME(dw, SomeDate) NOT IN ('Sunday', 'Saturday');
I have a report which looks for orders during a given date range... It returns the DateName from the report to give me mondays, it then gives me times from the report, to give me 859 for 08:59 for example.. I then use a case on this report to do the following...
WHEN (DATENAME(DW,T1.DocDate)) = 'Monday' AND T1.DocTime >= '700' AND T1.DocTime <= '859' THEN '1 Monday 07:00-08:59'
What I want to achieve, is the count for "1 Monday 07:00-08:59" to be divided by 2 if there has been 2 mondays, divided by 3 if there has been 3 mondays etc... but I have to be able to have it divide by 3 mondays for example, but 2 wednesdays if the date is a tuesday...
The report currently gives a Total of all orders placed in a datetime grouped together but has no division to average for each day.
SELECT (DATENAME(DW, T1.DocDate)) AS Weekday,
T1.DocTime AS Time,
SUM(T1.DocTotal) AS Value,
CASE
WHEN (DATENAME(DW, T1.DocDate)) = 'Monday'
AND T1.DocTime >= '700'
AND T1.DocTime <= '859' THEN '1 Monday 07:00-08:59'
FROM ORDR T1
INNER JOIN OCRD T0 ON T0.CardCode = T1.CardCode
WHERE (T1.DocDate >= #Start
AND T1.DocDate <= #End)
AND T0.QryGroup20 = 'Y'
AND T1.Canceled = 'N'
GROUP BY T1.DocTime,
T1.DocDate;
I expect the report to count the mondays / tuesdays etc that occur between #START and #END to give me an average sale per day and time.Then be able to then somehow divide by the count of the defined days in the case statement. (I'll hopefully be doing this part in Crystal Reports) but if i can get the first part, I'll work on the second half.
Just cross join the DW_Count subquery, which returns the count of every weekday in your orders range. Then you can count your avg for each weekday
SELECT (DATENAME(DW, T1.DocDate)) AS Weekday,
T1.DocTime AS Time,
SUM(T1.DocTotal) AS Value,
SUM(T1.DocTotal)/(CASE WHEN DATENAME(DW, T1.DocDate) = 'Monday' then DW_Count.MonCount
WHEN DATENAME(DW, T1.DocDate) = 'Tuesday' then DW_Count.TueCount
WHEN DATENAME(DW, T1.DocDate) = 'Wednesday' then DW_Count.WedCount
WHEN DATENAME(DW, T1.DocDate) = 'Thursday' then DW_Count.ThuCount
WHEN DATENAME(DW, T1.DocDate) = 'Friday' then DW_Count.FriCount
WHEN DATENAME(DW, T1.DocDate) = 'Saturday' then DW_Count.SatCount
WHEN DATENAME(DW, T1.DocDate) = 'Sunday' then DW_Count.SunCount) AS AvgValue
CASE
WHEN (DATENAME(DW, T1.DocDate)) = 'Monday'
AND T1.DocTime >= '700'
AND T1.DocTime <= '859' THEN '1 Monday 07:00-08:59'
FROM ORDR T1
INNER JOIN OCRD T0 ON T0.CardCode = T1.CardCode
CROSS JOIN (select sum(case when DATENAME(DW, DW_Count.DateValue) = 'Monday' then 1 else 0 end) as MonCount,
sum(case when DATENAME(DW, DW_Count.DateValue) = 'Tuesday' then 1 else 0 end) as TueCount,
sum(case when DATENAME(DW, DW_Count.DateValue) = 'Wednesday' then 1 else 0 end) as WedCount,
sum(case when DATENAME(DW, DW_Count.DateValue) = 'Thursday' then 1 else 0 end) as ThuCount,
sum(case when DATENAME(DW, DW_Count.DateValue) = 'Friday' then 1 else 0 end) as FriCount,
sum(case when DATENAME(DW, DW_Count.DateValue) = 'Saturday' then 1 else 0 end) as SatCount,
sum(case when DATENAME(DW, DW_Count.DateValue) = 'Sunday' then 1 else 0 end) as SunCount
from (select distinct DocDate from ORDR where (T1.DocDate >= #Start AND T1.DocDate <= #End))) AS DW_Count
WHERE (T1.DocDate >= #Start
AND T1.DocDate <= #End)
AND T0.QryGroup20 = 'Y'
AND T1.Canceled = 'N'
GROUP BY T1.DocTime,
T1.DocDate;
You can use the below logic-
For MSSQL
DECLARE #DT1 DATE = '20190802'
DECLARE #DT2 DATE = '20190820'
DECLARE #SatCount INT = 0
WHILE #DT1<= #DT2
BEGIN
SET #SatCount = #SatCount + CASE WHEN DATEPART(WeekDay,#DT1) = 7 THEN 1 ELSE 0 END
SET #DT1 = DATEADD(DD,1,#DT1)
END
SELECT #SatCount
Just one more option-
DECLARE #DT1 DATE = '20190802',
#DT2 DATE = '20190920';
SELECT SUM(CASE WHEN DATEPART(WeekDay,Date) = 7 THEN 1 ELSE 0 END)
FROM
(
SELECT TOP (DATEDIFF(DAY, #DT1, #DT2) + 1)
Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #DT1)
FROM sys.all_objects a
-- A System table just used for creating multiple rows.
CROSS JOIN sys.all_objects b
-- CROSS JOIN will create number of row = original number of row X original number of row.
)A
Can I find the date of a day that is on which dates the Saturdays and Sundays of a specific month fall? For e.g consider the month of JANUARY-2017. The following dates are weekend days:
7/1/2017 - Saturday
14/1/2017 - Saturday
21/1/2017 - Saturday
28/1/2017 - Saturday
1/1/2017 - Sunday
8/1/2017 - Sunday
15/1/2017 - Sunday
22/1/2017 - Sunday
29/1/2017 - Sunday
I want a SQL Server query for this such that when I pass in month and year as input, I should get back all the above dates (only dates of Saturday and Sunday) as output
I do not wish to use any user defined function and want to finish it in a single SELECT statement
Note: As already noted by another user in the comments, this query depends upon your server settings, namely DATEFIRST. If you need alterations to the query because of different settings, just tell me and I can change it around for you.
Using a CTE as dummy data...
/* Ignore this part...*/
WITH CTE AS
(
SELECT CAST('01/01/2017' AS DATE) AS [Date]
UNION ALL
SELECT DATEADD(DAY,1,[Date])
FROM CTE
WHERE DATE <= '12/31/2017'
)
/*Your actual SELECT statement would look like this, from your own table of course*/
SELECT
[Date]
,CASE DATEPART(dw,[Date])
WHEN 1 THEN 'Sunday'
WHEN 2 THEN 'Monday'
WHEN 3 THEN 'Tuesday'
WHEN 4 THEN 'Wednesday'
WHEN 5 THEN 'Thursday'
WHEN 6 THEN 'Friday'
WHEN 7 THEN 'Saturday'
END
FROM CTE
WHERE DATEPART(dw,[Date]) IN (1,7)
AND MONTH([Date]) = 12--<month>
AND YEAR([Date]) = 2017--<year>
OPTION (MAXRECURSION 0) -- You won't need this line if you're querying a real table
;
If running that works for you, then your real query would probably look something like this:
SELECT
[Date]
,CASE DATEPART(dw,[Date])
WHEN 1 THEN 'Sunday'
WHEN 2 THEN 'Monday'
WHEN 3 THEN 'Tuesday'
WHEN 4 THEN 'Wednesday'
WHEN 5 THEN 'Thursday'
WHEN 6 THEN 'Friday'
WHEN 7 THEN 'Saturday'
END
FROM < the table you want >
WHERE DATEPART(dw,[Date]) IN (1,7) -- Only Sundays and Saturdays
AND MONTH([Date]) = < the month you want >
AND YEAR([Date]) = < the year you want >
;
If you want to generate the data, then a CTE is the way to go. If you're passing parameters, it would look something like this:
DECLARE
#MONTH INT
,#YEAR INT
;
SET #MONTH = 1;
SET #YEAR = 2017;
WITH CTE AS
(
SELECT CAST(CAST(#MONTH AS VARCHAR(2)) + '/01/' + CAST(#YEAR AS VARCHAR(4)) AS [Date]) AS DATE
UNION ALL
SELECT DATEADD(DAY,1,[Date])
FROM CTE
WHERE DATE <= CAST(#MONTH AS VARCHAR(2)) +
CASE
WHEN #MONTH IN (9,4,6,11)
THEN '/30/'
WHEN #MONTH IN (1,3,5,7,8,10,12)
THEN '/31/'
WHEN #MONTH = 2 AND #YEAR/4.00 = #YEAR/4
THEN '/29/'
ELSE '/28/'
END
+ CAST(#YEAR AS VARCHAR(4))
)
SELECT
[Date]
,CASE DATEPART(dw,[Date])
WHEN 1 THEN 'Sunday'
WHEN 2 THEN 'Monday'
WHEN 3 THEN 'Tuesday'
WHEN 4 THEN 'Wednesday'
WHEN 5 THEN 'Thursday'
WHEN 6 THEN 'Friday'
WHEN 7 THEN 'Saturday'
END
FROM CTE
WHERE DATEPART(dw,[Date]) IN (1,7)
OPTION (MAXRECURSION 0)
;
Please try this one.
DECLARE #Year AS INT=2017,
#Month AS INT=3,
#FirstDateOfYear DATETIME,
#LastDateOfYear DATETIME
SELECT #FirstDateOfYear = DATEADD(yyyy, #Year - 1900, 0)
SELECT #LastDateOfYear = DATEADD(yyyy, #Year - 1900 + 1, 0)
-- Creating Query to Prepare Year Data
;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 ('Saturday','Sunday') and month(FromDate) = #Month
OPTION (MaxRecursion 370)
This should do the trick:
DECLARE #month date = '2017-01-01'
SET #month = dateadd(month, datediff(month, 0, #month), 0)
;WITH CTE as
(
SELECT 0 x
FROM (values(1),(1),(1),(1),(1),(1)) x(n)
),
CTE2 as
(
SELECT
top(day(eomonth(#month)))
-- use this syntax for sqlserver 2008
-- top(datediff(d, #month,dateadd(month,1,#month)))
cast(dateadd(d, row_number()over(order by(select 1))-1,#month) as date) cDate
FROM CTE CROSS JOIN CTE C2
)
SELECT
cDate,
datename(weekday, cDate) Weekday
FROM CTE2
WHERE
datediff(d,0,cDate)%7 > 4
Fiddle
From https://www.sqlservercentral.com/articles/finding-the-correct-weekday-regardless-of-datefirst, you simply:
(DATEPART(dw, #your_date) + ##DATEFIRST) % 7 NOT BETWEEN 2 AND 6
As requested, single select, language neutral, dateFirst neutral, almost SQL version neutral:
declare #OneDate datetime = '28/01/2017'; -- Any date from the target month/year
select MyDate -- raw date or ...
-- convert(varchar, MyDate, 103) + ' - ' + dateName(dw, MyDate) -- as Sample
as WeekEndDate
from (
select dateAdd(dd, number, dateAdd(mm, dateDiff(mm, 0, #OneDate), 0)) as MyDate
from master..spt_values
where type = 'P' and number < 31
) j
where 1 + (datePart(dw, MyDate) + ##DATEFIRST + 5) % 7 in (6, 7)
and month(MyDate) = month(#OneDate)
-- order by 1 + (datePart(dw, MyDate) + ##DATEFIRST + 5) % 7, MyDate -- as Sample
;
Another way to solve this problem as follow -
DECLARE #MONTH INT,#YEAR INT
SET #MONTH = 1;
SET #YEAR = 2017;
Declare #StartDate date =CAST(CAST(#MONTH AS VARCHAR(2)) + '/01/' + CAST(#YEAR AS VARCHAR(4)) AS [Date]), #EndDate date
Set #EndDate = EOMONTH(#StartDate)
Declare #Temp table (DateOfDay date, DaysName varchar(50))
While(#StartDate <= #EndDate)
Begin
Insert into #Temp
SELECT #StartDate DateOfMonth,
case when DATENAME(DW, #StartDate) = 'Saturday' then DATENAME(DW, #StartDate)
when DATENAME(DW, #StartDate) = 'Sunday' then DATENAME(DW, #StartDate)
end DaysName
set #StartDate = DATEADD(d,1,#StartDate)
End
select * from #Temp where DaysName is not null order by DaysName, DateOfDay
Can't you do something like this?
SELECT DATENAME(dw,'10/11/2016') AS DATE
WHERE DATE CONTAINS('Saturday') OR DATE CONTAINS('SUNDAY')
and instead of '10/11/2016' you only have to figure out how to generate all the dates in a month/year?
I've read nearly every question and I must be overlooking something simple, but I cannot get this to calculate correctly for all scenarios. I have a date, and a reference table of different integers that I need to add to specific dates on each row. So for the sake of this question, I've just defined them specifically instead of a more complicated query.
For Date A, I need to add 7 days to the date, but exclude weekends.
For Date B, I need to add 5 days, but same as above, exclude weekends.
I've referenced the code from here,
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SET #StartDate = '2008/10/01'
SET #EndDate = '2008/10/31'
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)
And applied it using my own logic to get this
SELECT '11/05/2015' AS [A Begin]
, 7 AS [A Add]
, CAST(DATEADD(day, ((DATEDIFF(wk, '11/05/2015', DATEADD(day, 7, '11/05/2015')) * 2)
+ (CASE WHEN DATENAME(dw, '11/05/2015') = 'Sunday' THEN 1 ELSE 0 END)
+ (CASE WHEN DATENAME(dw, DATEADD(day, 7, '11/05/2015')) = 'Saturday' THEN 2 ELSE 0 END) + 7), '11/05/2015') AS DATETIME)
+ CAST('16:00' AS DATETIME) AS [A End]
, '11/05/2015' AS [B Begin]
, 5 AS [B Add]
, CAST(DATEADD(day, ((DATEDIFF(wk, '11/05/2015', DATEADD(day, 5, '11/05/2015')) * 2)
+ (CASE WHEN DATENAME(dw, '11/05/2015') = 'Sunday' THEN 1 ELSE 0 END)
+ (CASE WHEN DATENAME(dw, DATEADD(day, 5, '11/05/2015')) = 'Saturday' THEN 2 ELSE 0 END) + 5), '11/05/2015') AS DATETIME)
+ CAST('16:00' AS DATETIME) AS [B End]
For Range A, its adding 7 days, but lands on a Saturday as the End.. which I cannot have, it needs to equal 11/16/2015 to be correct. Range B works just fine. I am not able to perform Declares or any other functions.
If I have 2 dates, I know I can work out how many days, hours, minutes etc are between the 2 dates using datediff, e.g:
declare #start datetime;
set #start = '2013-06-14';
declare #end datetime;
set #end = '2013-06-15';
select datediff( hour, #start, #end );
How do I figure out if the date range includes a weekend?
The reason why I want to know if the date range includes a weekend is because I want to subtract the weekend from the day or hour count. i.e. if the start day is Friday, and the end date is Monday, I should only get 1 days or 24 hours.
Datepart 1 = Sunday, and datepart 7 = Saturday on my server.
I have a function that calculates working days between 2 dates, the basic query is
declare #start datetime;
set #start = '2013-06-14';
declare #end datetime;
set #end = '2013-06-17';
SELECT
(DATEDIFF(dd, #Start, #end) +1) -- total number of days (inclusive)
-(DATEDIFF(wk, #Start, #end) * 2) -- number of complete weekends in period
-- remove partial weekend days, ie if starts on sunday or ends on saturday
-(CASE WHEN DATENAME(dw, #Start) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, #end) = 'Saturday' THEN 1 ELSE 0 END)
so you could work out if dates include weekend if working days different to datediff in days
SELECT case when (DATEDIFF(dd, #Start, #end) +1) <>
(DATEDIFF(dd, #Start, #end) +1) -- total number of days (inclusive)
-(DATEDIFF(wk, #Start, #end) * 2) -- number of complete weekends in period
-- remove partial weekend days, ie if starts on sunday or ends on saturday
-(CASE WHEN DATENAME(dw, #Start) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, #end) = 'Saturday' THEN 1 ELSE 0 END) then 'Yes' else 'No' end as IncludesWeekends
or simpler
SELECT (DATEDIFF(wk, #Start, #end) * 2) +(CASE WHEN DATENAME(dw, #Start) = 'Sunday' THEN 1 ELSE 0 END) +(CASE WHEN DATENAME(dw, #end) = 'Saturday' THEN 1 ELSE 0 END) as weekendDays
You have a weekend day if any one of the following three conditions is true:
The day of week (as an integer) of the end date is less than the day of week of the start date
Either day is itself a weekend day
The range includes at least six days
.
select
Coalesce(
--rule 1
case when datepart(dw,#end) - datepart(dw,#start) < 0 then 'Weekend' else null end,
-- rule 2
-- depends on server rules for when the week starts
-- I think this code uses sql server defaults
case when datepart(dw,#end) in (1,7) or datepart(dw,#start) in (1,7) then 'Weekend' else null end,
--rule 3
-- six days is long enough
case when datediff(d, #start, #end) >= 6 then 'Weekend' Else null end,
-- default
'Weekday')
One way, just showing you how you can use a table of numbers for this
declare #start datetime;
set #start = '2013-06-14';
declare #end datetime;
set #end = '2013-06-15'; -- play around by making this 2013-06-14 and other dates
IF EXISTS (SELECT * FROM(
SELECT DATEADD(dd,number,#start) AS SomeDAte
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(dd,number,#start) BETWEEN #start AND #end) x
WHERE DATEPART(dw,SomeDate) IN(1,7)) -- US assumed here
SELECT 'Yes'
ELSE
SELECT 'No'
Example to return all weekends between two dates
declare #start datetime;
set #start = '2013-06-14';
declare #end datetime;
set #end = '2013-06-30';
SELECT DATEADD(dd,number,#start) AS SomeDAte
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(dd,number,#start) BETWEEN #start AND #end
AND DATEPART(dw,DATEADD(dd,number,#start)) IN(1,7)
Results
2013-06-15 00:00:00.000
2013-06-16 00:00:00.000
2013-06-22 00:00:00.000
2013-06-23 00:00:00.000
2013-06-29 00:00:00.000
2013-06-30 00:00:00.000
You can use following functions. The first moves a given start- or enddate to monday(friday if backwards) if it begins in the weekend. The second calculates the seconds between two dates without weekends. Then you just need to check if the total-days equals the days without weksends(demo below).
CREATE FUNCTION [dbo].[__CorrectDate](
#date DATETIME,
#forward INT
)
RETURNS DATETIME AS BEGIN
IF (DATEPART(dw, #date) > 5) BEGIN
IF (#forward = 1) BEGIN
SET #date = #date + (8 - DATEPART(dw, #date))
SET #date = DateAdd(Hour, (8 - DatePart(Hour, #date)), #date)
END ELSE BEGIN
SET #date = #date - (DATEPART(dw, #date)- 5)
SET #date = DateAdd(Hour, (18 - DatePart(Hour, #date)), #date)
END
SET #date = DateAdd(Minute, -DatePart(Minute, #date), #date)
SET #date = DateAdd(Second, -DatePart(Second, #date), #date)
END
RETURN #date
END
GO
CREATE FUNCTION [dbo].[__DateDiff_NoWeekends](
#date1 DATETIME,
#date2 DATETIME
)
RETURNS INT AS BEGIN
DECLARE #retValue INT
SET #date1 = dbo.__CorrectDate(#date1, 1)
SET #date2 = dbo.__CorrectDate(#date2, 0)
IF (#date1 >= #date2)
SET #retValue = 0
ELSE BEGIN
DECLARE #days INT, #weekday INT
SET #days = DATEDIFF(d, #date1, #date2)
SET #weekday = DATEPART(dw, #date1) - 1
SET #retValue = DATEDIFF(s, #date1, #date2) - 2 * 24 * 3600 * ((#days + #weekday) / 7)
END
RETURN #retValue
END
Then you can get the info in this way:
declare #start datetime
set #start = '20130614'
declare #end datetime
set #end = '20130615'
declare #daysTotal int
declare #daysWoWeekends int
SET #daysTotal = DATEDIFF(dd, #start, #end)
SET #daysWoWeekends = dbo.__DateDiff_NoWeekends(#start, #end) / (24 * 3600)
SELECT CASE WHEN #daysTotal = #daysWoWeekends
THEN 'No weekend between'
ELSE 'There are weeksends' END,
#daysTotal,
#daysWoWeekends,#start,#end
Here's a demo: http://sqlfiddle.com/#!6/7cda7/11
There are weeksends 1 0 June, 14 2013 00:00:00+0000 June, 15 2013 00:00:00+0000
Here is the simple and generalize query . you can achieve result through recursive query . Check following query
with mycte as
(
select cast('2013-06-14' as datetime) DateValue
union all
select DateValue + 1 from mycte where DateValue + 1 < '2013-06-17'
)
select count(*) as days , count(*)*24 as hours
from mycte
WHERE DATENAME(weekday ,DateValue) != 'SATURDAY' AND
DATENAME(weekday ,DateValue) != 'SUNDAY'
OPTION (MAXRECURSION 0)
it will definitely work for you .
You can use a recursive CTE to get the dates between the range
WITH CTE_DatesTable
AS ( SELECT #MinDate AS [EffectiveDate]
UNION ALL
SELECT DATEADD(dd, 1, [EffectiveDate])
FROM CTE_DatesTable
WHERE DATEADD(dd, 1, [EffectiveDate]) <= #MaxDate )
SELECT [EffectiveDate]
FROM CTE_DatesTable
OPTION ( MAXRECURSION 0 );
and then Filter out the Weekends using ..
((DATEPART(dw, DT.EffectiveDate) + ##DATEFIRST) % 7) NOT IN (0, 1)
Similar problem, although in my case I wanted a onehot encoded binary list of columns (Mon-Sun) of whether two dates contain that DOW using Impala SQL.
select ...
max(case when tripduration > 6 then 1
when 2 >= dayofweek(tripstartdate) and 2 <= dayofweek(tripstartdate) + tripduration then 1
when 2 <= dayofweek(tripenddate) and 2 >= dayofweek(tripenddate) - tripduration then 1 else 0 end) as '2 mon',
max(case when tripduration > 6 then 1
when 3 >= dayofweek(tripstartdate) and 3 <= dayofweek(tripstartdate) + tripduration then 1
when 3 <= dayofweek(tripenddate) and 3 >= dayofweek(tripenddate) - tripduration then 1 else 0 end) as '3 tue',
max(case when tripduration > 6 then 1
when 4 >= dayofweek(tripstartdate) and 4 <= dayofweek(tripstartdate) + tripduration then 1
when 4 <= dayofweek(tripenddate) and 4 >= dayofweek(tripenddate) - tripduration then 1 else 0 end) as '4 wed',
max(case when tripduration > 6 then 1
when 5 >= dayofweek(tripstartdate) and 5 <= dayofweek(tripstartdate) + tripduration then 1
when 5 <= dayofweek(tripenddate) and 5 >= dayofweek(tripenddate) - tripduration then 1 else 0 end) as '5 thu',
max(case when tripduration > 6 then 1
when 6 >= dayofweek(tripstartdate) and 6 <= dayofweek(tripstartdate) + tripduration then 1
when 6 <= dayofweek(tripenddate) and 6 >= dayofweek(tripenddate) - tripduration then 1 else 0 end) as '6 fri',
max(case when tripduration > 6 then 1
when 7 >= dayofweek(tripstartdate) and 7 <= dayofweek(tripstartdate) + tripduration then 1
when 7 <= dayofweek(tripenddate) and 7 >= dayofweek(tripenddate) - tripduration then 1 else 0 end) as '7 sat',
max(case when tripduration > 6 then 1
when 1 >= dayofweek(tripstartdate) and 1 <= dayofweek(tripstartdate) + tripduration then 1
when 1 <= dayofweek(tripenddate) and 1 >= dayofweek(tripenddate) - tripduration then 1 else 0 end) as '1 sun'
from ....