calculating working hours in a month - sql

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

Related

SQL Server : calculate Bi-Weekly Pay Period Dates

I have the following to get bi-weekly pay period dates, The week starts on Sat
When I run it assuming today's date is 02/22/2016 I get the following
Pay Period Start: 02/13/2016
Pay Period End: 02/23/2016
But it should actually be 02/20/2016 - 3/4/2016 . It seems to be a week off.
If I start the week on Monday, then everything works fine.
What am I doing wrong, any help would be appreciated.
CREATE FUNCTION dbo.getFirstWeekDayDate (
#TargetDay DATETIME,
#strWeekDayName VARCHAR(25) --Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
)
RETURNS DATE
AS
BEGIN
DECLARE #Return DATE
-- Set to first of its year
SET #TargetDay = DATEADD(dd, -DATEPART(dayofyear, #TargetDay) + 1, #TargetDay)
;WITH Dates AS
(
SELECT #TargetDay AS DateVal
UNION ALL
SELECT DATEADD(d, 1, DateVal) AS DateVal
FROM Dates
WHERE DATEADD(d, 1, DateVal) < DATEADD(m, 1, #TargetDay)
)
SELECT #Return = MIN(DateVal)
FROM Dates
WHERE DATENAME(WEEKDAY,DateVal) = #strWeekDayName
RETURN #Return
END
GO
DECLARE
#periodstart date, #period int, #today date
set #today = '02/22/2016'
set #periodstart = dbo.getFirstWeekDayDate(#today,'Saturday') --get first sat
set #period = datediff(dd,#periodstart,#today)/14
select
#period AS period,
dateadd(dd, #period * 14, #periodstart) AS [payPeriodStart],
dateadd(dd, #period * 14 + 13, #periodstart) AS [payPeriodEnd]
Result
period payPeriodStart payPeriodEnd
----------- -------------- ------------
3 2016-02-13 2016-02-26
Ok. Thank you for all the comments. I decided to let the user specify first pay period end date in the settings and then I used that calculate the rest of the pay periods.

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.

T-SQL - Get start and end date based on week number.

Our business considers a week from (Monday - Sunday). I need to write a T-SQL function, which passes in year, week no as parameters and it will return the start and end date of that week. However I've seen many examples but the problem lies within the year overlapping.
e.g December 26, 2011 (Monday) - January 01, 2012 (Sunday)... << Would want to consider this as the last week of 2011.
And also in T-SQL the datepart(ww,DATE) considers Sunday as the start of the week??
Or Am I better creating my own table with the week no and storing its start and end date?
DECLARE
#Year INT,
#Week INT,
#FirstDayOfYear DATETIME,
#FirstMondayOfYear DATETIME,
#StartDate DATETIME,
#EndDate DATETIME
SET #Year = 2011
SET #Week = 52
-- Get the first day of the provided year.
SET #FirstDayOfYear = CAST('1/1/' + CAST(#YEAR AS VARCHAR) AS DATETIME)
-- Get the first monday of the year, then add the number of weeks.
SET #FirstMondayOfYear = DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(DAY, 6 - DATEPART(DAY, #FirstDayOfYear), #FirstDayOfYear)), 0)
SET #StartDate = DATEADD(WEEK, #Week - 1, #FirstMondayOfYear)
-- Set the end date to one week past the start date.
SET #EndDate = DATEADD(WEEK, 1, #StartDate)
SELECT #StartDate AS StartDate, DATEADD(SECOND, -1, #EndDate) AS EndDate
You should create a table with the holidays and days you donĀ“t wnat to consider in your counts.
After this count the days between the initial date and final date. (Step1)
Select in your table to check how many days are between your ini and final dates. (Step2)
Finally subtract the result of step 2 with result of step 1;
This is the sort of thing where you're better off creating a calendar table: the main issue is knowing how far into the past/future to populate it, but beyond that, have the table schema include week number and date. Depending on what other date-based queries you want to do, you might want to have additional column to break the date down into its constituant parts (e.g., have three separate columns for day/month/year/name of day, etc).
How about this one:
--DROP FUNCTION dbo.GetBusinessWeekStart
CREATE FUNCTION dbo.GetBusinessWeekStart(
#Year SMALLINT,
#Week TINYINT
)
RETURNS DATETIME
AS
BEGIN
DECLARE #FirstMonday TINYINT
DECLARE #Result DATETIME
IF ISNULL(#Week,0)<1 OR ISNULL(#Year,0)<1900
BEGIN
SET #Result= NULL;
END
ELSE
BEGIN
SET #FirstMonday=1
WHILE DATEPART(dw,CONVERT(DATETIME, '01/0' + CONVERT(VARCHAR,#FirstMonday) + '/' + CONVERT(VARCHAR,#Year)))<>2
BEGIN
SET #FirstMonday=#FirstMonday+1
END
SET #Result=CONVERT(DATETIME, '01/0' + CONVERT(VARCHAR,#FirstMonday) + '/' + CONVERT(VARCHAR,#Year))
SET #Result=DATEADD(d,(#Week-1)*7,#Result)
IF DATEPART(yyyy,#Result)<>#Year
BEGIN
SET #Result= NULL;
END
END
RETURN #Result
END
GO
--Example
SELECT dbo.GetBusinessWeekStart(2011,15) [Start],dbo.GetBusinessWeekStart(2011,15)+6 [End]

Get first Sunday of next month using T-SQL

Looking for a way to get the date in the format "11/1/2009", which would be the first sunday of next month. I want to run this query after the first sunday in october to get the first sunday of the upcoming month. What is the best method to accomplish this with a T-SQL query?
Thanks
try this:
Declare #D Datetime
Set #D = [Some date for which you want the following months' first sunday]
Select DateAdd(day, (8-DatePart(weekday,
DateAdd(Month, 1+DateDiff(Month, 0, #D), 0)))%7,
DateAdd(Month, 1+DateDiff(Month, 0, #D), 0))
EDIT Notes:
The first of next Month is given by the expression:
DateAdd(Month, 1+DateDiff(Month, 0, #D), 0)
or by:
which can be modified to give the first of the month two months from now by changing the 1 to a 2:
DateAdd(Month, 2+DateDiff(Month, 0, #D), 0)
EDIT: In response to #NissanFan, and #Anthony: to modify this to return the first Monday Tuesday Wednesday, etc, change the value 8 to a 9, 10, 11, etc....
Declare #Sun TinyInt Set #Sun = 8
Declare #Mon TinyInt Set #Mon = 9
Declare #Tue TinyInt Set #Tue = 10
Declare #Wed TinyInt Set #Wed = 11
Declare #Thu TinyInt Set #Thu = 12
Declare #Fri TinyInt Set #Fri = 13
Declare #Sat TinyInt Set #Sat = 14
Declare #D Datetime, #FONM DateTime -- FirstofNextMonth
Set #D = [Some date for which you want the following months' first sunday]
Set #FONM = DateAdd(Month, 1+DateDiff(Month, 0, #D),0)
Select
DateAdd(day, (#Sun -DatePart(weekday, #FONM))%7, #FONM) firstSunInNextMonth,
DateAdd(day, (#Mon -DatePart(weekday, #FONM))%7, #FONM) firstMonInNextMonth,
DateAdd(day, (#Tue -DatePart(weekday, #FONM))%7, #FONM) firstTueInNextMonth,
DateAdd(day, (#Wed -DatePart(weekday, #FONM))%7, #FONM) firstWedInNextMonth,
DateAdd(day, (#Thu -DatePart(weekday, #FONM))%7, #FONM) firstThuInNextMonth,
DateAdd(day, (#Fri -DatePart(weekday, #FONM))%7, #FONM) firstFriInNextMonth,
DateAdd(day, (#Sat -DatePart(weekday, #FONM))%7, #FONM) firstSatInNextMonth
Just an FYI rather then coming up with some code to do this how about using a calendar table.
Take a look at this: http://web.archive.org/web/20070611150639/http://sqlserver2000.databases.aspfaq.com/why-should-i-consider-using-an-auxiliary-calendar-table.html
This also may help:
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=99696
Here is a query to get first working day of next month
DECLARE #DAYOFWEEK INT,#ReminderDate DateTime
SET #DAYOFWEEK = DATEPART( WEEKDAY,DateAdd(D,- Day(GetDate())+1, DATEADD(M,1,GetDate())) )
Print #DAYOFWEEK
If #DAYOFWEEK = 1
Set #ReminderDate = DateAdd(D,- Day(GetDate())+2, DATEADD(M,1,GetDate()))
Else If #DAYOFWEEK =7
Set #ReminderDate = DateAdd(D,- Day(GetDate())+3, DATEADD(M,1,GetDate()))
Else
Set #ReminderDate = DateAdd(D,- Day(GetDate())+1, DATEADD(M,1,GetDate()))
Print #ReminderDate
Reference taken from this blog:
SQL Server 2012 introduced one new TSQL EOMONTH to return the last day of the month that contains the specified date with an optional offset.
CREATE TABLE tbl_Test_EOMONTH
(
SampleDate DATETIME
)
GO
INSERT INTO tbl_Test_EOMONTH VALUES ('2015-12-20')
INSERT INTO tbl_Test_EOMONTH VALUES ('2015-11-08')
INSERT INTO tbl_Test_EOMONTH VALUES ('2015-10-16')
INSERT INTO tbl_Test_EOMONTH VALUES ('2015-09-26')
INSERT INTO tbl_Test_EOMONTH VALUES ('2016-01-31')
GO
SELECT
DATEADD(DAY,8-DATEPART(WEEKDAY,DATEADD(DAY,0,EOMONTH([SampleDate])))
,EOMONTH([SampleDate])) AS FirstSunday_ofTheNextMonth
FROM tbl_Test_EOMONTH
GO
You can use DATENAME to determine the day you want, I might recommend a loop to move the date from the 01 of the month in question to get to the first sunday.
So lets try:
DECLARE #DateTime DATETIME
Set to the date to start off with, then add 1 day until you find what you are looking for. Use datename with dw...
We have used this to determine weekends, but holidays will be a problem, where we use a table to store that.
Try this code as a function:
-- Variables
DECLARE #DATE DATETIME
DECLARE #DAY INT
DECLARE #DAYOFWEEK INT
DECLARE #TESTDATE DATETIME
-- Set
SET #TESTDATE = GETDATE()
SET #DATE = DATEADD( MONTH, 1, #TESTDATE )
SET #DAY = DATEPART( DAY, #TESTDATE )
SET #DATE = DATEADD( DAY, -#DAY + 1, #DATE )
SET #DAYOFWEEK = DATEPART( WEEKDAY, #DATE )
IF #DAYOFWEEK > 1
BEGIN
SET #DAYOFWEEK = 8 - #DAYOFWEEK
END
ELSE
BEGIN
SET #DAYOFWEEK = 0
END
SET #DATE = DATEADD( DAY, #DAYOFWEEK, #DATE )
-- Display
PRINT #TESTDATE
PRINT #DAY
PRINT #DAYOFWEEK
PRINT #DATE
Here is the non-system specific way to determine the first Sunday of the following month:
First, get the current month and add one month.
Next, set the date of that variable to be on the first.
Next, find the day value of that date (let's assume Mondays are 1 and Sundays are 7).
Next, subtract the day value of the 1st of the month from the day value of Sunday (7).
You now have the number of days between the first of the month and the first Sunday. You could then add that to the date variable to get the first Sunday, or, since we know the first of the month is 1, you could just add one to the difference (found in that last step above) and that is the date of the first Sunday. (You have to add one because it's subtracting and thus if the first of the given month IS Sunday, you'd end up with 0).
I have been looking through the T-SQL documentation and it is not at all intuitive as to how how you would use my method, but you will need the concept of "day of week number" to make it work no matter what.
This would be simplest with an auxiliary calendar table. See this link, for example.
However, it can be done without one, though it's rather tricky. Here I assume you want the first future date that is the first Sunday of a month. I've written this with a variable #now for - well - now, so it's easier to test. It might be possible to write more simply, but I think this has a better chance of being correct than quickly-written simpler solutions. [Corrected by adding DAY(d) < 8]
SET #now = GETDATE();
WITH Seven(i) AS (
SELECT -1 UNION ALL SELECT 0 UNION ALL SELECT 1
UNION ALL SELECT 3 UNION ALL SELECT 4
UNION ALL SELECT 5 UNION ALL SELECT 6
), Candidates(d) AS (
SELECT DATEADD(WEEK,i+DATEDIFF(WEEK,'19000107',#now),'19000107')
FROM Seven
)
SELECT TOP (1) d AS SoonestFutureFirstSunday
FROM Candidates
WHERE DAY(d) < 8 AND MONTH(d) >= MONTH(#now)
AND (MONTH(d) > MONTH(#now) OR DAY(d) > DAY(#now))
ORDER BY d; ORDER BY d;
I reckon that the answer is this
SELECT DATEADD(Month, DATEDIFF(Month, 0, GETDATE()) + 1, 0) + 6 - (DATEPART(Weekday,
DATEADD(Month,
DATEDIFF(Month,0, GETDATE()) + 1, 0))
+ ##DateFirst + 5) % 7 --FIRST sunday of following month

Calculating in SQL the first working day of a given month

I have to calculate all the invoices which have been paid in the first 'N' days of a month. I have two tables
. INVOICE: it has the invoice information. The only field which does matter is called 'datePayment'
. HOLYDAYS: It is a one column table. Entries at this table are of the form "2009-01-01",
2009-05-01" and so on.
I should consider also Saturdays and Sundays
(this might be not a problem because I could insert those days at the Hollidays table in order to consider them as hollidays if neccesary)
The problem is to calculate which is the 'payment limit'.
select count(*) from invoice
where datePayment < PAYMENTLIMIT
My question is how to calculate this PAYMENTLIMIT. Where PAYMENTLIMIT is 'the fifth working day of every month'.
The query should be run under Mysql and Oracle therefore standard SQL should be used.
Any hint?
EDIT
In order to be consistent with the title of the question the pseudo-query should the read as follows:
select count(*) from invoice
where datePayment < FIRST_WORKING_DAY + N
then the question can be reduced to calculate the FIRST_WORKING_DAY of every month.
You could look for the first date in a month, where the date is not in the holiday table and the date is not a weekend:
select min(datePayment), datepart(mm, datePayment)
from invoices
where datepart(dw, datePayment) not in (1,7) --day of week
and not exists (select holiday from holidays where holiday = datePayment)
group by datepart(mm, datePayment) --monthnr
Something like this might work:
create function dbo.GetFirstWorkdayOfMonth(#Year INT, #Month INT)
returns DATETIME
as begin
declare #firstOfMonth VARCHAR(20)
SET #firstOfMonth = CAST(#Year AS VARCHAR(4)) + '-' + CAST(#Month AS VARCHAR) + '-01'
declare #currDate DATETIME
set #currDate = CAST(#firstOfMonth as DATETIME)
declare #weekday INT
set #weekday = DATEPART(weekday, #currdate)
-- 7 = saturday, 1 = sunday
while #weekday = 1 OR #weekday = 7
begin
set #currDate = DATEADD(DAY, 1, #currDate)
set #weekday = DATEPART(weekday, #currdate)
end
return #currdate
end
I'm not 100% sure about whether the "weekday" numbers are fixed or might depend on your locale on your SQL Server. Check it out!
Marc
Rather than a Holidays table of days to exclude, we use the calendar table approach: one row for every day the application will ever need (thirty years spans a modest 11K rows). So not only does it have an is_weekday column, it has other things relevant to the enterprise e.g. julianized_date. This way, every possible date would have a ready-prepared value for first_working_day_this_month and finding it involves a simple lookup (which SQL products tend to be optimized for!) rather than 'calculating' it each time on the fly.
We have dates table in our application (filled with all dates and date parts for some tens of years), what allows various "missing" date manipulations, like (in pseudo-sql):
select min(ourdates.datevalue)
from ourdates
where ourdates.year=<given year> and ourdates.month=<given month>
and ourdates.isworkday
and not exists (
select * from holidays
where holidays.datevalue=ourdates.datevalue
)
Ok, at a first stab, you could put the following code into a UDF and pass in the Year and Month as variables. It can then return TestDate which is the first working day of the month.
DECLARE #Month INT
DECLARE #Year INT
SELECT #Month = 5
SELECT #Year = 2009
DECLARE #FirstDate DATETIME
SELECT #FirstDate = CONVERT(varchar(4), #Year) + '-' + CONVERT(varchar(2), #Month) + '-' + '01 00:00:00.000'
DROP TABLE #HOLIDAYS
CREATE TABLE #HOLIDAYS (HOLIDAY DateTime)
INSERT INTO #HOLIDAYS VALUES('2009-01-01 00:00:00.000')
INSERT INTO #HOLIDAYS VALUES('2009-05-01 00:00:00.000')
DECLARE #DateFound BIT
SELECT #DateFound = 0
WHILE(#DateFound = 0)
BEGIN
IF(
DATEPART(dw, #FirstDate) = 1
OR
DATEPART(dw, #FirstDate) = 1
OR
EXISTS(SELECT * FROM #HOLIDAYS WHERE HOLIDAY = #FirstDate)
)
BEGIN
SET #FirstDate = DATEADD(dd, 1, #FirstDate)
END
ELSE
BEGIN
SET #DateFound = 1
END
END
SELECT #FirstDate
The things I don`t like with this solution though are, if your holidays table contains all days of the month there will be an infinite loop. (You could check the loop is still looking at the right month) It relies upon the dates being equal, eg all at time 00:00:00. Finally, the way I calculate the 1st of the month past in using string concatenation was a short cut. There are much better ways of finding the actual first day of the month.
Gets the first N working days of each month of year 2009:
select * from invoices as x
where
datePayment between '2009-01-01' and '2009-12-31'
and exists
(
select
1
from invoices
where
-- exclude holidays and sunday saturday...
(
datepart(dw, datePayment) not in (1,7) -- day of week
/*
-- Postgresql and Oracle have programmer-friendly IN clause
and
(datepart(yyyy,datePayment), datepart(mm,datePayment))
not in (select hyear, hday from holidays)
*/
-- this is the MSSQL equivalent of programmer-friendly IN
and
not exists
(
select * from holidays
where
hyear = datepart(yyyy,datePayment)
and hmonth = datepart(mm, datePayment)
)
)
-- ...exclude holidays and sunday saturday
-- get the month of x datePayment
and
(datepart(yyyy, datePayment) = datepart(yyyy, x.datePayment)
and datepart(mm, datePayment) = datepart(mm, x.datePayment))
group by
datepart(yyyy, datePayment), datepart(mm, datePayment)
having
x.datePayment < MIN(datePayment) + #N -- up to N working days
)
Returns the first Monday of the current month
SELECT DATEADD(
WEEK,
DATEDIFF( --x weeks between 1900-01-01 (Monday) and inner result
WEEK,
0, --1900-01-01
DATEADD( --inner result
DAY,
6 - DATEPART(DAY, GETDATE()),
GETDATE()
)
),
0 --1900-01-01 (Monday)
)
SELECT DATEADD(day, DATEDIFF (day, 0, DATEADD (month, DATEDIFF (month, 0, GETDATE()), 0) -1)/7*7 + 7, 0);
select if(weekday('yyyy-mm-01') < 5,'yyyy-mm-01',if(weekday('yyyy-mm-02') < 5,'yyyy-mm-02','yyyy-mm-03'))
Saturdays and Sundays are 5, 6 so you only need two checks to get the first working day