SQL - Exluding Bank Holidays from date count for month - sql

For this month (Jan) the working days excluding bank holidays is 20.
Excluding bank holidays would take this to 19. I'm happy with this.
NOTE: I've manually added new years day in this example, I will be using a table which specifically has all bank holidays stored
This becomes troublesome when a month has multiple bank holidays (May). Because this feeds a daily run report, the bank holiday can't be excluded until the date has been reached/passed. (The report runs each day showing the previous day - e.g runs on the 2nd showing the 1st)
Using May as the example:
BH = 8th,25th
Report runs on the 7th showing 4 Working days
Report runs on the 8th showing 5 Working days
Report runs on the 11th showing 5 Working days
Report runs on the 22nd showing 14 Working days
Report runs on the 25th showing 15 Working days (Excludes this and previous BH)
Report runs on the 26th showing 15 Working days
CODE:
Declare #ReportedDays int
Declare #StartDate datetime
Declare #Enddate datetime
Declare #Days int
Declare #BankHoliday date
Declare #Working int
Set #ReportedDays = DAY(GETDATE())
Set #StartDate = DATEADD(DAY,(DATEPART(DAY,GETDATE())-1)*(-1),GETDATE())
Set #Enddate = GETDATE()
Set #BankHoliday = '2020-01-01'
set #Days =
datediff(dd, #StartDate, #EndDate) - (datediff(wk, #StartDate, #EndDate) * 2) -
case when datepart(dw, #StartDate) = 1 then 1 else 0 end +
case when datepart(dw, #EndDate) = 1 then 1 else 0 end
Set #Working = CASE when GETDATE() >= #BankHoliday then #Days - 1 Else #Days end
Select
#Working as 'Excluding Bank Holidays',
#Days as 'Excluding Weekends',
#ReportedDays 'Current(DAY)Date'
From Table1

Related

How can I calculate the date of the upcoming second Monday in January relative to the current date in SQL?

How can I calculate the date of the upcoming second Monday in January relative to the current date using a SQL query?
Below is a query that I have which doesn't work if the current date is of the same year but prior to the upcoming second Monday of January.
declare #secondMondayOfJan date = dateadd(day , ((17 - datepart(dw,current_timestamp)) % 7) + 6, current_timestamp)
IF current_timestamp > #secondMondayOfJan
select #secondMondayOfJan as Deadline
ELSE
declare #firstDayOfNextYear date = datefromparts(year(current_timestamp)+1,1,1)
set #secondMondayOfJan = dateadd(day, ((17 - datepart(dw,#firstDayOfNextYear)) % 7) + 6, #firstDayOfNextYear)
select #secondMondayOfJan as Deadline
This looks quite verbose, but hopefully makes the logic clear, and doesn't depend on any particular settings (such as DATEFIRST or language settings):
;With Nums(n) as (
select 0 union all select 1 union all select 2 union all select 3
union all select 4 union all select 5 union all select 6
), ThisAndNext as (
select
DATEADD(year,DATEDIFF(year,'20010101',GETDATE()),'20010108') as SecondWeek,
0 as Choose
union all
select
DATEADD(year,DATEDIFF(year,'20010101',GETDATE()),'20020108'),
1
), Combined as (
select DATEADD(day,n,SecondWeek) as Possible,Choose
from ThisAndNext cross join Nums
)
select top 1 Possible from Combined
where Possible >= DATEADD(day,DATEDIFF(day,0,GETDATE()),0)
and DATEPART(weekday,Possible) = DATEPART(weekday,'20150504')
order by Choose
Nums is just a small set of numbers - it can be replaced with a select from a numbers table, if you already have one.
ThisAndNext finds the 8th of this year and next year, as two separate rows.
Combined adds between 0 and 6 days onto the 8th of this year and next.
Finally, we select the first date from Combined that is (the following bullet points correspond to the lines of the WHERE clause)
equal to or greater than today's date (using another instance of the DATEADD/DATEDIFF pattern to remove the time element from GETDATE())
on a Monday (by comparing it to an arbitrary, well known Monday)
by preference, from this year rather than next
All taken together, this means that we have selected a future (or current) day that falls between the 8th and the 14th of January that is a Monday. If you don't want "today" to be a possible result, just change the >= comparison towards the end to be a >.
The logic behind this code is:
get the first day of the current year
get the first day of the next year
find the 2nd weekday of jan of the current year
find the 2nd weekday of jan of the next year
compare the current date if is greater than or less than or equal and based on this set the deadline to the correct 2nd weekday
It's setup to retrieve the 2nd monday, but changing the value of the variable #dw between 1 to 7, you will get the others weekday
(1 = Sunday, 2 = Monday, 3 = Tuesday, 4 = Wednesday, 5 = Thursday, 6 = Friday, 7 = Saturday)
You can play with this code at:
http://rextester.com/TVKYW75798
/*
THIS STATEMENT WILL RETURN THE 2ND WEEKDAY OF JAN BASED ON CURRENT DATE.
IF CURRENT_DATE = 2ND WEEKDAY OF JAN
THEN DEADLINE = CURRENT_DATE
IF CURRENT_DATE < 2ND WEEKDAY OF JAN
THEN DEADLINE = 2ND WEEKDAY OF JAN (CURRENT YEAR)
IF CURRENT_DATE > 2ND WEEKDAY OF JAN
THEN DEADLINE = 2ND WEEKDAY OF JAN (NEXT YEAR)
YOU CAN PLAY WITH VARIABLE #date and #dw TO TEST.
I LEAVE COMMENTED IN THE CODE FOUR SCENARIOS (YEAR 2015)
WHERE YOU WILL BE ABLE TO CHECK THE CONDITIONS ABOVE.
2015-01-12 WAS THE 2ND MONDAY OF JAN
*/
DECLARE #date DATE = GETDATE()
-- SET #date = '2015-01-01'
-- SET #date = '2015-01-11'
-- SET #date = '2015-01-12'
-- SET #date = '2015-01-13'
DECLARE #currentYearFirstDay DATE = CAST(datepart(yy, #date) AS VARCHAR) + '-01-01'
DECLARE #nextYearFirstDay DATE = DATEADD(yy, 1, #currentYearFirstDay)
DECLARE #d DATE
DECLARE #secondWeekdayCurrentYear DATE
DECLARE #secondWeekdayNextYear DATE
DECLARE #dw INT = 2 /*USE VALUES FROM 1 TO 7, WHERE 1 = SUNDAY AND 7 = SATURDAY*/
DECLARE #weekOfYear INT = DATEPART(ww,#d)
DECLARE #weekDay INT = DATEPART(dw,#d)
DECLARE #checkNextYear INT = 0
SET #d = #currentYearFirstDay
GOTO GET_2nd_WEEKDAY_FROM_FIRST_DAY_OF_YEAR
GET_2nd_WEEKDAY_FROM_FIRST_DAY_OF_YEAR:
BEGIN
SET #weekDay = DATEPART(dw, #d)
/*FIRST DAY OF YEAR GREATER THAN WEEKDAY, THEN 2nd WEEKDAY WILL BE THE 3rd WEEK*/
IF #weekDay > #dw
BEGIN
SET #d = DATEADD(ww, 2, #d)
END
/*FIRST DAY OF YEAR LESS THAN OR EQUAL WEEKDAY, THEN 2nd WEEKDAY WILL BE THE 2nd WEEK*/
ELSE
BEGIN
SET #d = DATEADD(ww, 1, #d)
END
IF #checkNextYear = 0
BEGIN
SET #secondWeekdayCurrentYear = DATEADD(dw, -(#weekDay - #dw), #d)
SET #d = #nextYearFirstDay
SET #checkNextYear = 1
GOTO GET_2nd_WEEKDAY_FROM_FIRST_DAY_OF_YEAR
END
ELSE
BEGIN
SET #secondWeekdayNextYear = DATEADD(dd, -(#weekDay - #dw), #d)
GOTO EXIT_BLOCK
END
END
EXIT_BLOCK:
SELECT CASE
WHEN #date > #secondWeekdayCurrentYear
THEN #secondWeekdayNextYear
ELSE
CASE
WHEN #date = #secondWeekdayCurrentYear
THEN #date
ELSE #secondWeekdayCurrentYear
END
END AS DEADLINE

How to get database values by week

I just want to ask how can I achieve getting the records in a database by week in a month? using sql
What I really mean is when I input 1 it will get the records in 1st week of a month
I've done a lot of research today but it seems I can't find a good solution
Heres the code:
DECLARE #MONTH int
DECLARE #YEAR int
DECLARE #WEEK int
SET #MONTH = 9
SET #YEAR = 2013
SET #WEEK = 1
SELECT RH.RepairOrderNumber FROM dbo.RepairOrderHeader RH
WHERE MONTH(RH.DateReleased) = #MONTH AND YEAR(RH.DateReleased) = #YEAR AND WEEK(RH.DateReleased) = #WEEK
I just want to fetched the records according to month,year, and by week is there any way and precise code on how to do this?
DATEPART(WEEK,) gives you number of a week in an YEAR so you just need to calculate number of week in the month calculating difference between current date week number and the week number of the first day of the month:
WHERE MONTH(RH.DateReleased) = #MONTH
AND YEAR(RH.DateReleased) = #YEAR
AND DATEPART(WEEK,RH.DateReleased)
-DATEPART(WEEK,DATEADD(DAY,-DAY(RH.DateReleased)+1,RH.DateReleased))+1
= #WEEK
WEEK returns the week of the year, starting on a constant day of the week (i.e. each Sunday). You seem to want the week of the month, which I guess would always start on the 1st of each month.
so:
SELECT RH.RepairOrderNumber FROM dbo.RepairOrderHeader RH
WHERE MONTH(RH.DateReleased) = #MONTH AND
YEAR(RH.DateReleased) = #YEAR AND
DAY(RH.DateReleased) / 7 + 1 = #WEEK
Where possible avoid using functions on columns in the WHERE clause. You immediately lose the ability to take advantage of indices on these columns. The below solution gives the same results as the select answer, but performs much better:
DECLARE #MONTH INT = 9
DECLARE #YEAR INT = 2013
DECLARE #WEEK INT = 2
--1ST OF THE MONTH DECLARED
DECLARE #Date DATE = CAST(CAST(#Year AS VARCHAR(4)) +
RIGHT('0' + CAST(#Month AS VARCHAR(2)), 2) + '01' AS DATE)
SET #Date = DATEADD(WEEK, #Week - 1, DATEADD(DAY, 1 - DATEPART(WEEKDAY, #Date), #Date));
SELECT RH.RepairOrderNumber
FROM dbo.RepairOrderHeader RH
WHERE RH.DateReleased >= #Date
AND RH.DateReleased < DATEADD(WEEK, 1, #Date);
All this does is get the first of the month in question, then find the first day of the week that the first of the month falls in. Then adds the declared number of weeks to this date.
With an example table with 6 years of data in and 10 rows for each day (20,480 records, so not a lot) and an index on your date column the execution plans of this and the accepted answer are as follows (Accepted on top, using dates on the bottom):
Testing DDL and queries on SQL-Fiddle
This shows that when you use WEEK(DateColumn) = #Week that the index is not used at all, and the query cost is much higher. Depending on the distribution of data in your table this could make a massive difference, even in the small set of data in my example it is 800% more efficient.
I consider '2013-08-26' - '2013-09-01' the first week in september
DECLARE #MONTH int = 9
DECLARE #YEAR int = 2013
DECLARE #WEEK int = 1
DECLARE #from date= dateadd(day, datediff(day, -(#WEEK - 1)*7,
(dateadd(month, (#year-1900) * 12 + #month - 1 , 0)))/7*7 , 0)
SELECT RH.RepairOrderNumber
FROM dbo.RepairOrderHeader RH
WHERE RH.DateReleased >= #from
and RH.DateReleased < dateadd(week, 1, #from)

Returning Database values dependent on days not dates

Hi I'm trying to get a database to return some values, specifically, I need to return every invoice which is dated between the previous Monday to Sunday.
The reason for this is that this query will be ran on varying days, eg I need to return the results for the week Monday 5th August - Sunday 11th August at some point in the following week (12th - 18th).
I originally had this set up as a simple WHERE:
[WHERE TaxDate >= (GETDATE() - 7)]
because the invoices were done every monday so it worked itself out, but now they could be done any day of the following week. Are there any functions that I can use to do this?
Thanks
Look at the datepart function with the day of week parameter:
WHERE Datepart(dw, TaxDate) = 2
declare #currentdow int
declare #delta int
declare #startdate datetime
declare #endtime datetime
set #delta = 0
set #startofweek = DatePart(dw, getDate())
while #scurrentdow <> 2
begin
set #delta = #delta + 1
set #currentdow = #currentdow - 1
if #currentdow < 1
begin
set #currentdow = 7
end
end
set #startdate = DateSubtract(day,GetDate(), delta)
set #enddate = DateAdd(day, #startdate, 6)
long winded, but close I think.

calculating working hours in a month

I am working on an attendance software in asp.net, in it i have to make a report which will tell the user about the hours and everything...so far i have created the basic functionality of the system, i.e. the user can check in and check out...i am stuck at making the report...
I have to calculate the working hours for every month, so the user can compare his hours with the total hours...what i had in mind was to create a stored procedure which when given a month name and a year, returns an int containing working hours for that month....but i can seem to get at it....
so far i found out how to create a date from a given month and a date, and found out the last day of that month, using which i can find out the total days in month...now i cant seem to figure out how do i know how much days to subtract for getting the working days.
here's the so far code..
declare
#y int,
#m int,
#d int,
#date datetime
set #y = 2012
set #m = 01
set #d = 01
----To create the date first
select #date = dateadd(mm,(#y-1900)* 12 + #m - 1,0) + (#d-1)
----Last Day of that date
SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,#date)+1,0))
any help will be appreciated guys, thanks in advance....
The #theDate is any date on the month you want to calculate the work days. This approach does not take care about holidays.
DECLARE #theDate DATETIME = GETDATE()
SELECT MONTH(#theDate) [Month], 20 + COUNT(*) WorkDays
FROM (
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, #theDate), 28) AS theDate
UNION
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, #theDate), 29)
UNION
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, #theDate), 30)
) AS d
WHERE DATEPART(DAY, theDate) > 28
AND DATEDIFF(DAY, 0, theDate) % 7 < 5
Here you can consider the below sql server code to get the first and
last day of the given month and also ignore all the Saturdays and Sundays.
DECLARE #curr_date datetime=getdate()
DECLARE #st_date datetime,#ed_date datetime
select #st_date=DATEADD(mm,datediff(mm,0,#curr_date),0),#ed_date = DATEADD(mm,datediff(mm,-1,#curr_date),-1)
--select #st_date as first_day,#ed_date as last_day
SET DATEFIRST 1 --Monday as first day of week
select DATEADD(dd,number,#st_date) from master..spt_values
where DATEDIFF(dd,DATEADD(dd,number,#st_date),#ed_date) >= 0 and type='P'
and DATEPART(DW,DATEADD(dd,number,#st_date)) <> 6
and DATEPART(DW,DATEADD(dd,number,#st_date)) <> 7
But inorder to calculate the actual working hours, you will have to take into the consideration of following thigs
1.Calculate the time interval between swipe-in and swipe-outs between start and end time for a day.
2.Exclude all the time gap(employee not in office)
3.Consider the company holidays.
etc
Here is a UDF to count work days. You can pass any date of a month to this function. But usually you should use actual "calendar" table to calculate work days and insert in this table weekends, holidays,... and so on.
CREATE FUNCTION dbo.WorkDaysCount (#Date datetime)
RETURNS int AS
BEGIN
DECLARE #BeginOfMonth datetime
SET #BeginOfMonth=DATEADD(DAY,-DAY(#Date)+1,#Date);
DECLARE #EndOfMonth datetime
SET #EndOfMonth=DATEADD(Day,-1,DATEADD(Month,1,#BeginOfMonth));
DECLARE #cDate datetime
set #cDate=#BeginOfMonth
Declare #WorkDaysCount int
SET #WorkDaysCount=0
while #cDate<=#EndOfMonth
begin
if DATEPART(dw,#cDate) not in (1,7) SET #WorkDaysCount=#WorkDaysCount+1 -- not a Sunday or Saturday change (1,7) to (6,7) if you have other week start day (Monday).
set #cDate=#cDate+1;
end;
return (#WorkDaysCount);
END

How to find the average (in terms of date/day) in sql server?

I want to find the result as such like
I want to take current date from system .
I want to retrieve corresponding day of that date.
and go back 90 days back to the current date and want to try to find out that how many times similar day had occured.
want to find out total patients visisted on those days to clinic. (ex.COUNT(VisitId)) from my PtientVisist table.
and finally want to calaculate the average of patients visited on that day.
like-> if I get todays date 8 jun 2012 and retrieved today day as Friday . so want to find out
like since from last 90 days from todays date how many fridays had occured and how many patiens visited on that total fridays and want to count AVG = Total patients visisted/ total friday.
Please give some assistance Thanks in advance.
Air code to get the # of days of the week in a timespan:
DECLARE #enddate datetime
SET #enddate = GETDATE()
DECLARE #dayofweek nvarchar(50)
SELECT #dayofweek = DATENAME(dw, getdate())
DECLARE #startdate datetime
SELECT #startdate = DATEADD(d,-90, getdate())
DECLARE #count int
SET #count = 0
WHILE (#startdate < #enddate)
BEGIN
IF (DATENAME(dw, #startdate) = #dayofweek)
BEGIN
SET #count = #count + 1
END
SET #startdate = DATEADD(d, 1, #startdate)
END
PRINT #count
You can do something with the actual date in the loop in order to get your final query.