I have a query where I am calculating total days between two days including start and end date by the following SQL query. If the end date is not null, then end date is considered as current date.
This query does the job. But I do not want to count Sat and Sundays. Possible public UK Holidays.(I can do this one, if I can get the logic for Saturdays and Sundays)
SELECT DateDiff(day,DateADD(day,-1,StartDate),ISNULL(EndDate,getDate()))numberOfDays
FROM <mytable>
How do I count only weekdays between two dates?
Thank you
I would strongly recommend a calendar table for this, especially if you need to take specific holidays into account. Calculating Easter dynamically, for example, is going to be a royal pain.
http://web.archive.org/web/20070611150639/http://sqlserver2000.databases.aspfaq.com/why-should-i-consider-using-an-auxiliary-calendar-table.html
If you're going to use T-SQL alone, be careful about using functions that rely on regional/language settings for the output of things like DATENAME ...
Take a look at the DATEDIFF MSDN page.
At the bottom of the page, there is some user-generated content.
One user posted a function there which does exactly what you want, including holidays (headline: "UDF to return the number of business days, including a check to a bank holidays table").
try this
SELECT DateDiff(day,DateADD(day,-1,StartDate),ISNULL(EndDate,getDate())) -
( CASE WHEN DATENAME(dw, StartDate) = 'Sunday' OR
DATENAME(dw,ISNULL(EndDate,getDate())) = 'Sunday' THEN 1 ELSE 0 END)
- ( CASE WHEN DATENAME(dw, StartDate) = 'Saturday' OR
DATENAME(dw,ISNULL(EndDate,getDate())) = 'Saturday' THEN 1 ELSE 0 END)
numberOfDays
FROM <mytable>
Related
I am trying to figure out how to use the Data-Driven Subscription portion of SSRS to fire out a report to a bunch of people via email on the third business day of the month.
I'm a bit new to SQL but am learning very quickly, this just happens to be out of my small realm of knowledge.
I do have a table full of days of months, what year, what month, days of the week and all of that in different date formats. I just can't figure it out, I feel it's within my grasp of understanding though.
So far this is what I have and I feel like this could be summarized into a different easier sql statement? More optimized I guess.
select distinct --(CASE --when day_of_week = (2,3,4,5,6) then dateadd(day,1,day_desc_01) --when day_of_week = (7) then dateadd(day,2,day_desc_01) else day_of_week end) as 'BD_Date' day_of_week , day_desc_01 , date from Company.dbo.Company_Calendar where year = 2023 and day_of_week not in (1,7) and date <> '1900-01-01' and day_weekday_ct = 1 and year = 2023
I just want it to return the 3rd business day of the month for every month. Then probably a statement that says if it is the 3rd business day, fire off the report, if not, do nothing. I hope this makes a little bit of sense? I could also be way off track on this and way in over my head.
Thank you for your time and help!
This is a generic exmaple, but you should be able to apply the logic to your company calendar table.
This query will simply give you all the 3rd business days in the month. It filters out weekends and public holidays (assuming you have some way of recording the public holidays in your table). Once the filters are applied, if just assigns a row number to each records within in each calendar year/month and the only returns those where it is 3.
There are some extra columns included for clarity.
You may be able to simplify this but I dont; know what your calendar table contains so this assumes it has just the date (TheDate) and a column to indicate PublicHoliday.
CREATE VIEW ThirdBusDay AS
SELECT
*
FROM (
SELECT
TheDate
, DayOfWeek = DATEPART(WeekDay, TheDate)
, TheYear = YEAR(TheDate)
, TheMonth = MONTH(TheDate)
, TheDay = DAY(TheDate)
, WorkingDayOfMonth = ROW_NUMBER() OVER(PARTITION BY YEAR(TheDate), MONTH(TheDate) ORDER BY TheDate)
FROM myCalendarTable
WHERE
DATEPART(WeekDay, TheDate) NOT IN (7,1) -- Filter out Saturday and Sunday, change this if system week start is not Sunday.
AND
PublicHoliday = 0 -- filter out public holidays etc..
) d
WHERE WorkingDayOfMonth = 3
So if CAST(GetDate() AS Date) exists in this view then you know you have to execute the report.
I'm running the following query
SELECT a.DAT
FROM a
and getting a column of datetimes. I would like to change this column or alternatively add a column, that adds a workday to the corresponding datetime and I want to be able to filter for the date at the same time
My approach for changing the workday is like this CASE WHEN DATENAME(dw, date) = 'Friday' THEN DATEADD(dw, 3, date), but I don't know how to fit that in. Here I'm adding 3 days to Friday because it should skip to Monday the next workday, in the other cases I would add only 1 day, for Monday to Thursday.
Maybe there is a better way?
In this image I tried to show how the result of an examplatory query should
look like. I have all dates available but I want to filter for the 14th, which should show me the 11 dates, because of the added workday. Since June 11 is Friday
I think my Main problem is I don't know how make the extra column depending on the given datetime, it should add 3 on Fridays and 1 on every other day (Saturday and Sunday don't exist in the base data)
Thanks for your responses.
I have a solution now. If it can be easier, let me know, if you like. Anyway, thanks for your help everyone
Solution:
DECLARE #Date DATETIME
Set #Date = '14/06/2021'
SELECT
a.DAT,
(CASE WHEN DATENAME(dw, a.DAT) = 'Friday' THEN DATEADD(DAY, 3, a.DAT) ELSE DATEADD(DAY, 1, a.DAT) END) as RealDate
FROM a
WHERE (CASE WHEN DATENAME(dw, #Date) = 'Monday' THEN DATEADD(DAY, -3, #Date) ELSE DATEADD(DAY, -1, #Date) END) = a.DAT
You seem to be describing a case expression:
SELECT (CASE WHEN DATENAME(weekday, date) = 'Friday'
THEN DATEADD(DAY, 3, date)
ELSE DATEADD(DAY, 1, date)
END) as next_date
Welcome to S/O. If your table already has a datetime column, that is probably best. Dont try to break things down such as a date column, a time column, and some workday column. There are plenty of functions that allow you to extract parts as needed. However, your post tags only show SQL instead of a specific database such as sql-server, mysql, oracle, etc. but appears SQL-Server via DateAdd() function.
With date functions you could do things like filtering based on the "Day" of the given date/time column NOT being a Sat or Sun. Or, filtering on a single month by a >= first day of the month but less than first day of following month. This way you dont have to worry about time factors. This type of query would allow inclusion of working up to 11:59:59pm before following day.
If you edit your question with any additional clarifications on why adding 3 days per your example may help offer additional resolution. Try not to just put things that could be long in comments. Then just comment back on my post and I'll try to follow-up (in addition to others that may follow this post).
I have created a stored procedure to generate a delivery day when a customer requests it.
Depending on the zone of the customer it outputs the delivery day.
But now I also want to add date to it. So it not only output what day, but also the date on that day in the upcoming week.
This is what my stored procedure looks like now (customerId is my IN parameter)
BEGIN
SELECT
CASE customer_zone
WHEN 1 THEN 'monday'
WHEN 2 THEN 'tuesday'
WHEN 3 THEN 'wednesday'
WHEN 4 THEN 'thursday'
WHEN 5 THEN 'friday'
ELSE 'unknown'
END
FROM customer
WHERE customer_id = customerId;
END
It works fine, but I really don't know how to handle the date feature - I guess I can use the now() function and add some number to it according to the relative day?
It looks like your customer_zone is a number from 1 to 5. Perhaps you can simply add the customer_zone to NOW() or CURRENT_DATE() to get the relative date. Something like this:
SELECT DATE_ADD(CURRENT_DATE(), INTERVAL customer_zone DAY) AS my_date
FROM customer
The first Monday in the future is always
dateadd(d,datediff(d,'1900-01-01',getdate())/7*7+7,'1900-01-01')
You can add (customer_zone - 1) days to this to get the first M/T/W/T/F for customer_zone 1/2/3/4/5. Depending on your SQL implementation, you may need now(), today(), etc., instead of getdate().
dateadd(d,datediff(d,'1900-01-01',getdate())/7*7+7+customer_zone-1,'1900-01-01')
If you google SQL with "next monday", you'll find a lot of useful stuff.
How can I create a stored procedure that accepts a start and end date.(e.g April 1 - April 30
1.) Get the business days including Saturdays x (a value). +
2.) Get Holidays x (a value)
and return the total.
I'm new to this, I guess it would be a tsql function. hmm.
any help would be appreciated.
Thanks
The simplest solution to this problem is to create a Calendar table that contains a value for every day you might want to consider. You could then add columns that indicate whether it is a business day or a holiday. With that, the problem becomes trivial:
Select ..
From Calendar
Where IsBusinessDay = 1
And Calendar.[Date] Between '2010-04-01' And '2010-04-30'
If you wanted the count of days, you could then do:
Select Sum( Case When IsBusinessDay = 1 Then 1 Else 0 End ) As BusinessDayCount
, Sum( Case When IsHoliday = 1 Then 1 Else 0 End ) As HolidayCount
From Calendar
Where Calendar.[Date] Between '2010-04-01' And '2010-04-30'
http://classicasp.aspfaq.com/date-time-routines-manipulation/how-do-i-count-the-number-of-business-days-between-two-dates.html
First, you will need to store all of the holidays into an independant table (Christmas, Easter, New Year Day, etc. with their respective dates (normally timed at midnight));
Second, you will have to generate, into a temporary table maybe, the dates of the office days, it then excludes the dates contained in the Holidays table.
Third, you may set the office hours to these dates depending on what day it is, if you have different working hours on different day.
That is the algorithm for you to find the appropriate code implementation.
Let me know if this helps!
I realise different solutions will have different variations of what "Working Days" means but in my case I mean Monday to Friday inclusive.
Basically I have Created a function to do the calculation for me and my current solution works. My concern (and reason for asking this question) is that I am worried that this is a bad way of achieving this because the function is being called with a very high frequency. In the last 3 months it has been called 12 million times on a production system, with the average worker time 44ms.
This lead me to wonder if this is the correct way of achieving solution.
Firstly here is the function I created:
CREATE FUNCTION [dbo].[fn_WorkDays]
(
#StartDate DATETIME,
#EndDate DATETIME = NULL --#EndDate replaced by #StartDate when DEFAULTed
)
RETURNS INT
AS
BEGIN
--===== Declare local variables
--Temporarily holds #EndDate during date reversal
DECLARE #Swap DATETIME
--===== If the Start Date is null, return a NULL and exit
IF #StartDate IS NULL
RETURN NULL
--===== If the End Date is null, populate with Start Date value
-- so will have two dates (required by DATEDIFF below)
IF #EndDate IS NULL
SELECT #EndDate = #StartDate
--===== Strip the time element from both dates (just to be safe) by converting
-- to whole days and back to a date. Usually faster than CONVERT.
-- 0 is a date (01/01/1900 00:00:00.000)
SELECT #StartDate = DATEADD(dd,DATEDIFF(dd,0,#StartDate),0),
#EndDate = DATEADD(dd,DATEDIFF(dd,0,#EndDate) ,0)
--===== If the inputs are in the wrong order, reverse them
IF #StartDate > #EndDate
SELECT #Swap = #EndDate,
#EndDate = #StartDate,
#StartDate = #Swap
--===== Calculate and return the number of workdays using the
-- input parameters. This is the meat of the function.
-- This is really just one formula with a couple of parts
-- that are listed on separate lines for documentation
-- purposes.
RETURN (
SELECT
--Start with total number of days including weekends
(DATEDIFF(dd,#StartDate,#EndDate)+1)
--Subtact 2 days for each full weekend
-(DATEDIFF(wk,#StartDate,#EndDate)*2)
--If StartDate is a Sunday, Subtract 1
-(CASE WHEN DATENAME(dw,#StartDate) = 'Sunday'
THEN 1
ELSE 0
END)
--If EndDate is a Saturday, Subtract 1
-(CASE WHEN DATENAME(dw,#EndDate) = 'Saturday'
THEN 1
ELSE 0
END)
)
END
As a simple example of its use I would run this type of query:
SELECT MYTABLE.EntryDate
,dbo.fn_WorkDays(MYTABLE.EntryDate, getutcdate()) as WorkingDays
FROM MYTABLE
MyTable could contain 5000 rows all with different Dates in the EntryDate Column (5000 calls to Function)
My question is I am missing something here in the way that I am doing this, would it be beneficial to create a lookup table for this (but that is a lot of combinations of dates)
Any thoughts, improvements or recommendations would be appreciated...
I don't think there's a lot you can do with the UDF tbh - having it calculated at run-time like this in SQL is always going to incur a hit to some degree.
So, ideally (and this may not be possible as I don't know the full picture), I think what I'd do is store the WorkingDays number in your table and calculate it ONCE when the record is created. If that's not possible (i.e. when the record is created, you don't have an "end date" so it has to be worked out using "now") then I'd be considering a nightly scheduled job to go and recalculate all those particular records so that they are updated each day - then when an "end date" does get entered, that record does not get included in this batch update.
The benefits of this, are you offload the calculations to a quieter period, and only do the calculations once per day. The query becomes a lot simpler and more performant as it can just read the WorkingDays number from the table.
If that's not an option, then I'd suggest doing the calculations in the front end, remove the hit from the DB.
There's two problems here:
Calculating the number of days between two dates,
Identifying whether or not a give Date is a "business day".
The second includes easy ones like "weekday" versus "weekend", holidays (secular, religious, and legal), etc.
You'll need to solve both.
The first is easier, because relational databases will have functions to help you. It's the second that's harder and more variable, because it changes by locale and business.