sql caculating the 'x' businessday of the month - sql-server

if I wanted to write a query that said I want to know the 4th business day of the current month. How would you do that? I'm not sure where to start mainly because July 4th is messing me up...
Thanks!

Most enterprises have an internal calendar which denotes the days that the organisation is working. This'll typically exclude Saturdays, Sundays and any public holidays that the organisation chooses to observe.
My first piece of advice is to see if such a calendar is available for your use. If it is, your query becomes trivial.
Assuming a table like so:-
CREATE TABLE [dbo].[CalendarDays](
[CalendarDayId] [int] IDENTITY(1,1) NOT NULL,
[CalendarDate] [datetime] NOT NULL,
[IsWorkingDay] [bit] NOT NULL,
)
..your query becomes...
SELECT MIN(CalendarDate) FROM CalendarDays
WHERE CalendarDate >= '2013-01-01' AND IsWorkingDay = 1
The good news is that even if your org doesn't have a calendar, you can create one anyway, filling it with your best understanding of what is and isn't a working day.

A simple loop with some logic to increment the date by a day and check for week days and not holidays will do the trick.
BEGIN
-- some table with known holiday values
DECLARE #tblHolidays TABLE(holiday DATETIME)
INSERT INTO #tblHolidays(holiday) VALUES ('2013-7-4')
-- parameters
DECLARE #month DATETIME
DECLARE #businessDay INT
SET #month = '2013-7-1'
SET #businessDay = 5
-- logic, loop and find only business days
DECLARE #count INT
SET #count = 0
WHILE 1=1
BEGIN
IF DATENAME(weekday, #month) IN ('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday')
AND #month NOT IN (SELECT * FROM #tblHolidays)
BEGIN SET #count = #count + 1 END
IF #count < #businessDay
BEGIN SET #month = #month + 1 END
ELSE
BEGIN BREAK END
END
SELECT #month
END

Try the below Query... it will help you....
DECLARE #startdate DATETIME
DECLARE #count INT
DECLARE #NDay INT
SET #startdate='07/01/2013' --Date of the month
SET #count=0
set #NDay = 4 --Nth businessday
WHILE #count < #NDay
BEGIN
IF Datename(dw, #startdate) NOT IN ( 'Saturday', 'Sunday' )
BEGIN
SET #startdate=Dateadd(dd, 1, #startdate)
SET #count=#count + 1
END
ELSE
BEGIN
SET #startdate=Dateadd(dd, 1, #startdate)
END
END
SELECT #startdate - 1 'BussinessDay'
it results 2013-07-04 00:00:00.000 .... 4th Business working Day of July

Related

Find out the quarter based on the financial year in SQL Server stored procedure

I have two tables, one is Period and the other is UserTarget.
Period table is:
QuarterNo StartDate EndDate
-----------------------------------------
1 2018-04-01 2018-06-30
2 2018-07-01 2018-09-30
3 2018-10-01 2018-12-31
4 2019-01-01 2019-03-31
UserTarget table is :
USERID YEAR QTR AMOUNT
---------------------------------
akshay 2019 1 200
Right now I am taking the qtr no from period table. Now I don't need take qtr no from period table. I want it from stored procedure based on year is entered in usertarget table
Existing stored procedure :
ALTER PROCEDURE [dbo].[GetQuarterlyTargetData]
#Userid VARCHAR(50)
AS
BEGIN
DECLARE #QuarterNumber VARCHAR(50)
DECLARE #SetTarget DECIMAL(10);
DECLARE #StartDate DATE
DECLARE #EndDate DATE
SELECT
#QuarterNumber = p.QuarterNo,
#SetTarget = AMOUNT
FROM
PERIOD p
LEFT OUTER JOIN
USERTARGETS s ON p.QuarterNo = s.QTR
WHERE
StartDate <= GETDATE() AND EndDate >= GETDATE()
SELECT
#StartDate = StartDate,
#EndDate = EndDate
FROM
PERIOD
WHERE
QuarterNo = #QuarterNumber
From this procedure I am getting the start date and end date for quarter but I don't want to modify in period table every time when I want to check previous years data.
I believe that the term you are looking for is fiscal year. It's where the company year is different than the calendar year.
Note that many people recommend using a lookup table instead of calculating it. Date matches can be difficult for SQL to optimize.
Here's one way to do it. Finding the fiscal year and quarter would probably be good to put in a table function.
DECLARE #userTarget TABLE (UserId VARCHAR(20), Year INT, Quarter INT, Amount INT)
INSERT INTO #userTarget
VALUES
('akshay', 2018, 4, 150)
,('akshay', 2019, 1, 200)
SELECT
s.UserId
,s.Amount
,FY.FiscalYear
,FQ.FiscalQuarter
FROM
(
SELECT
--DATEFROMPARTS(2019, 2, 23)
GETDATE()
AS reportDate
) AS ReportDate
CROSS APPLY (
SELECT
CASE WHEN MONTH(ReportDate.reportDate) < 4 THEN YEAR(ReportDate.reportDate) - 1 -- Fiscal Year begins in April
ELSE YEAR(ReportDate.reportDate)
END AS FiscalYear
) AS FY
CROSS APPLY (
SELECT
DATEDIFF(QUARTER, DATEFROMPARTS(FY.FiscalYear, 4, 1), ReportDate.reportDate) + 1 AS FiscalQuarter
) AS FQ
INNER JOIN #userTarget s
ON s.Year = FY.FiscalYear
AND s.Quarter = FQ.FiscalQuarter
Also, be careful with end dates. last_day >= GETDATE() does not include the last day. Take as an example the end of last quarter, it would calculate it as '2019-03-31 00:00' >= '2019-03-31 08:20' which is false when you want it to be true.
After thinking I come up to this solutions
ALTER PROCEDURE [dbo].[Dashboard_GetQuarterlyTargetData]
#Userid varchar(50)
AS
Begin
DECLARE #QuarterNumber varchar(50)
DECLARE #SetTarget decimal(10);
DECLARE #AchievedTarget decimal(10);
DECLARE #TargetProgress decimal(10);
DECLARE #RemainingTarget decimal(10);
DECLARE #StartDate Date
DECLARE #EndDate Date
DECLARE #Year as int
Select #QuarterNumber = QTR,#Year=YEAR,#SetTarget=AMOUNT from USERTARGETS where USERID=#Userid
if(#QuarterNumber = 1)
begin
SELECT #StartDate = DATEFROMPARTS(#year,4,1), #EndDate=DATEFROMPARTS(#year,6,30)
End
else if(#QuarterNumber = 2)
begin
SELECT #StartDate = DATEFROMPARTS(#year,7,1), #EndDate=DATEFROMPARTS(#year,9,30)
End
else if(#QuarterNumber = 3)
begin
SELECT #StartDate = DATEFROMPARTS(#year,10,1), #EndDate=DATEFROMPARTS(#year,12,31)
End
else if(#QuarterNumber = 4)
begin
SELECT #StartDate = DATEFROMPARTS(#year,1,1), #EndDate=DATEFROMPARTS(#year,3,31)
End

check if a date exists for a particular month in sql server

I am working on a project where I need to set plan's start and end dates.
Plans are to be setup in an order so that plan's renewal date should be same for all months(or after desired months).
If a plan(monthly) starts on 11th of june, its expiry date should be 10 july so that it can continue from next 11 and then next 11 and so on. And if plan is annual for the same startdate it should expire on 10th of june so that new start date would be 11th of june again and so on.
Here I need to check if expiry date does not exists, like if a plans starts from 31st of jan then we don't have a date 30th feb(its expiry date) in this scenario the expiry date should be set to 28 of feb and if a leap year should be 29th feb(last of invalid date's month).
For the same I am looking for a sql server udf function which except 2 parameters a datetime and number of months. Here it should return a new date(expiry date) after adding months(which is 2nd parameter).
Trying below but not getting the logic how to proceed it.
--print dbo.[Bill_FnDateFormatPackageEndDate]('2018-06-11 12:25',1)
CREATE function [dbo].[Bill_FnDateFormatPackageEndDate]
(
#datetime datetime,
#months int
)
returns nvarchar(40)
begin
declare #date nvarchar(40)
declare #testdate nvarchar(40)
declare #day nvarchar(2)
declare #month nvarchar(2)
declare #year nvarchar(4)
declare #time nvarchar(10)
if ISDATE(#datetime)=1
begin
set #day = day(#datetime)
set #month = month(#datetime)+1
set #year = year(#datetime)
set #testdate =#year+'/'+#month+'/'+#day
if ISDATE(#testdate)=1
set #date=DATEADD(day, -1, #testdate )
end
return #date
end
This query will give you the last date based on the startdate and the number of months. You can convert this into a UDF.
select case when day(DATEADD(month, #nMonths, #StartDate)) = day(#StartDate) then dateadd(day, -1, DATEADD(month, #nMonths, #StartDate)) else DATEADD(month, #nMonths, #StartDate) end
I have solved it, may be not the best of solutions but its working as i needed.
--print dbo.[Bill_FnDateFormatPackageEndDate]('2018-06-09 0:00',1)
Alter function [dbo].[Bill_FnDateFormatPackageEndDate]
(
#startdate datetime,
#months int
)
returns nvarchar(40)
begin
declare #date nvarchar(40)
declare #testdate datetime
declare #day int
declare #month int
declare #year int
declare #NoOfDays int
if ISDATE(#startdate)=1
begin
set #day = day(#startdate)
set #month = month(#startdate)+#months
set #year = year(#startdate)
if #month>12
begin
set #month=#month-12
set #year=#year+1
end
set #testdate=cast( (cast(#year as nvarchar(4))+'-'+cast(#month as nvarchar(2))+'-'+cast(1 as nvarchar(2))+' 0:00') as datetime)
set #NoOfDays= datediff(day, dateadd(day, 1-day(#testdate), #testdate), dateadd(month, 1, dateadd(day, 1-day(#testdate), #testdate)))
if #NoOfDays>=#day
begin
set #testdate =cast( cast((cast(#year as nvarchar(4))+'-'+cast(#month as nvarchar(2))+'-'+cast(#day as nvarchar(2))+' 0:00') as nvarchar(40)) as datetime)
set #date=DATEADD(day, -1, cast(#testdate as datetime))
end
else
set #date =cast( cast((cast(#year as nvarchar(4))+'-'+cast(#month as nvarchar(2))+'-'+cast(#NoOfDays as nvarchar(2))+' 0:00') as nvarchar(40)) as datetime)
end
return #date--#date
end

How to check if a month and year lies between 2 dates

I have a 2 columns in a table startdate and enddate and I need to create a function get all ID which lies between the date data passed in function.
my function input parameters are
#Year int,
#Month int = null,
#Quarter int = null
now if month is null I need to check only with date which is easy but if month is provided how to check if it lies between startdate and enddate or else if #Quarter is provided I need to check if 3 months of the year collides with startdate and enddate .
What I have written upto now is
CREATE FUNCTION GetAssociatesEmpID(
#Year int,
#Month int = null,
#Quarter int = null
)
RETURNS TABLE
AS BEGIN
IF #Month IS NOT NULL -- Monthly Statistics
BEGIN
END
ELSE IF #Quarter IS NOT NULL -- Quarterly Statistics
BEGIN
END
ELSE -- Yearly Statistics
BEGIN
return SELECT ID FROM Table WHRER #Year>=YEAR(startdate) AND #Year<=YEAR(enddate)
END
END
Kindly help me with condition with month and Quarter
Quarter has 4 possible inuts range between 1-4
and its month range is between #Quarter*3-3 and #Quarter*3
Start by creating two local DateTime variables. (Or DateTime2, or whatever data type your table's start and end date columns are using.) Maybe call them #WhereStartDate and #WhereEndDate.
Use some IF statements to populate your new #WherexxxDate variables. For example, if Month is provided, something like:
DECLARE #Year int = 2016;
DECLARE #Month int = 3;
DECLARE #WhereStartDate datetime;
DECLARE #WhereEndDate datetime;
SET #WhereStartDate = CONVERT( datetime, CAST(#Year as char(4)) + '/' + CAST(#Month as varchar(2)) + '/01');
SET #WhereEndDate = DATEADD( day, -1, DATEADD( month, 1, #WhereStartDate ));
SELECT #WhereStartDate, #WhereEndDate;
Once you have actual date/time variables, you can write your query appropriately...
SELECT ...
...
WHERE startDate >= #WhereStartDate
AND enddate <= #WhereEndDate
This has the added benefit of being sargable. The way that you have written your query is non-sargable. (In short, non-sargable queries will not make use of indexes properly and will have poor performance. If the table is large, the resulting table scans could take a very long time.)
Not where I can test, but this should be close...
WHERE
(#Year >= YEAR(startdate))
AND
(#Year <= YEAR(startdate))
AND
(
( (#Month IS NOT NULL) AND (#Month >= MONTH(startdate)) AND (#Month <= MONTH(startdate)) )
OR
( (#Month IS NULL) AND (#Quarter >= DATEPART(QUARTER, startdate)) AND (#Quarter <= DATEPART(QUARTER, startdate)) )
)

SQL Server Check if day does not fall on weekend and if so, iterate to a weekday

I am using SQL Server with t-Sql
I have the following code that checks to see if a date falls on a weekend
and if it does, it will iterate until the day falls on a weekday
Declare #ProDate as Date
set #ProDate = '08/05/12'
WHILE (DATEPART(DW, #ProDate) = 1 OR DATEPART(DW, #ProDate) = 7 )
BEGIN
set #ProDate = DATEADD(day, 1, #ProDate)
END
select #ProDate
The code seems to work. Wondering if I missed anything or if there is a better way to handle this.
This code is dependent on the setting of DATEFIRST in your system.
I'd add a SET DATEFIRST 7 before the date checks
Alternately, this avoids the while loop
declare #df int = ##Datefirst
set datefirst 1
select
case when DATEPART(DW, #ProDate)>=6 then
DATEADD(d, 8-DATEPART(DW, #ProDate), #prodate)
else #ProDate
end
set DATEFIRST #df
This code will work. It is almost identical to code that we use in a heavily used function.
The only suggestion that I might have is do you need to integrate a Holiday check? We have a Holiday table to store dates that need to be skipped as well.
Use below code to get next wrking date after excluding weekends and Holidays
Declare #AddDay as integer = 3
Declare #NextWorkingDate DateTime
Declare #StartDate DateTime = Cast(getdate() as date)
While #AddDay > 0
begin
Select #NextWorkingDate = #StartDate + #AddDay +
(datediff(wk, #StartDate, #StartDate+ #AddDay ) * 2) -- add weekend
--Exclude weekend
If datepart(dw,#NextWorkingDate ) = 1 or datepart(dw,#NextWorkingDate ) = 7 --Add 2 days if target date is either Saturday or Sunday
set #NextWorkingDate = #NextWorkingDate + 2
--Count no of holidays if falling within start date and nextwrking date
Select #AddDay = Count(*) from HolidayTable ST --Holiday list
where ST.OffDate between #StartDate+1 and #NextWorkingDate
Set #StartDate = #NextWorkingDate
End
Select #NextWorkingDate
USE below to exclude weekeends and Holiday
Declare #AddDay as integer = 3
Declare #NextWorkingDate DateTime
Declare #StartDate DateTime = Cast(getdate() as date)
While #AddDay > 0
begin
Select #NextWorkingDate = #StartDate + #AddDay +
(datediff(wk, #StartDate, #StartDate+ #AddDay ) * 2) -- add weekend
--Exclude weekend
If datepart(dw,#NextWorkingDate ) = 1 or datepart(dw,#NextWorkingDate ) = 7 --Add 2 days if target date is either Saturday or Sunday
set #NextWorkingDate = #NextWorkingDate + 2
--Count no of holidays if falling within Hold days/Deposit days
Select #AddDay = Count(*) from HolidayTable ST --Holiday list
where ST.OffDate between #StartDate+1 and #NextWorkingDate
Set #StartDate = #NextWorkingDate
End
Select #NextWorkingDate

sql query calculating no of employees joined each financial year i.e from 1-04-2002 to 31-03-2003

I have a table in which joining dates are give in datetime format.
I have to calculate how many employees joined each financial year resp. ie for eg from
1-04-2002 to 31-03-2003.this should work for each year..from 2003 to 2004,2004 to 2005...n so on.
can anybdy help?
thanxx.
You can map start date to financial year using YEAR(DATEADD(M,-3,JoinDate) I think and you can count records with a CTE, e.g.
with EmployeeStartFinYear(FinYear, EmployeeId)
as
(
select year(dateadd(M,-3,JoinDate)), EmployeeId
from Employees
where JoinDate is not null
)
select FinYear, count(EmployeeId)
from EmployeeStartFinYear
group by FinYear
order by FinYear;
Here's my answer. I think it looks horrid, but i think it works. I'll explain the logic behind it.
I declared the Start and End dates just because it's easier to write than a date.
The #Years variable is the difference in years between the start and end date.
#Counter is used to loop through the number of years stored in #Years. #Diff is always one more than #Counter because each time we go through the loop, we want to increment the date range so it's always 1 year, rather than be counting employees that joined in 1 year, then 2 years etc.
#TempTable stores the info we get each time we go through the query.
All the query does is get the count of employees between the Start Date and a year from that start date and puts it into a temp table. Then it looks through again, and gets the employees that started between Start Date + 1 and Start Date + 2.
Sorry if it's horrible and ugly and doesn't work.
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
DECLARE #Years TINYINT
DECLARE #Counter TINYINT
DECLARE #Diff TINYINT
DECLARE #TempTable TABLE
(
FinancialYear VARCHAR(9)
,Employees TINYINT
)
SET #Count = 0
SET #Diff = 1
SET #Years = DATEDIFF(yyyy, #StartDate, #EndDate)
WHILE #Count < #Years - 1
BEGIN
SELECT
CAST(DATEPART(yyyy, DATEADD(yyyy, #Count, #StartDate) AS VARCHAR(4)) + '-' + CAST(DATEPART(yyyy, DATEADD(yyyy, #Diff, #StartDate)) AS VARCHAR(4) AS FinancialYear
,COUNT(employee_id) AS Employees
INTO #TempTable
FROM
Employees
WHERE
join_date >= #StartDate AND join_date < DATEADD(yyyy, 1, #StartDate)
GROUP BY
CAST(DATEPART(yyyy, DATEADD(yyyy, #Count, #StartDate) AS VARCHAR(4)) + '-' + CAST(DATEPART(yyyy, DATEADD(yyyy, #Diff, #StartDate)) AS VARCHAR(4)
SET #Count = #Count + 1
SET #Diff = #Diff + 1
END
SELECT * FROM #TempTable

Resources