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)) )
)
Related
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
I have the query like this:
SELECT table.a, table.b, table.c from Table table
How i can cast "table.b" to UTC time just adding CAST into the query?
SELECT
table.a,
**%CAST_TO_UTC_FUNCTION to table.b%**,
table.c
from Table table
I'm not able to cast it separately and declaring local variables.
You can write your query as follows:
SELECT
table.a,
dateAdd(
second,
dateDiff(second, getDate(), getUtcDate()),
table.b) as b_converted_to_UTC,
table.c
from Table table
This converts the values in column bto UTC, by adding to those values the tmie difference that currently exists between the local date (getDate()) and the UTC date (getUtcDate()).
In order for the above query to work, the following must be true:
the date(s) stored in column b must be expressed in local time
the server date/time zone should be properly configured
I solved the problem by creating a temporary table that stores the date range and the current UTC offset.
Here is the partial solution that works for US starting at year 2007, for regions that observe DST.
It can be easily modified to for years before 2007.
Please see the end of the solution for a sample usage
-- GET UTC for time in US for region that observe Daylight Saving Time (meaning will not work for part of Arizona and Hawaii)
DECLARE
#DSTStartDay datetime = '2007-03-1 02:00:00',
#DSTEndDay datetime = '2007-11-1 02:00:00',
#i int = 0,
#currDate datetime,
#offset int
DECLARE
#DST TABLE (StartDate datetime, EndDate datetime, DSTOffset int)
-- Insert into #DST DSTOffset of 0 if not DST date range, and -1 if DST date as temporary offset value
-- Then when we get Time Zone Offset, we can update DSTOffset to actual current offset (hours)
WHILE #i < 20
BEGIN
INSERT #DST
SELECT DATEADD(d,
15 - CASE DATEPART(dw, #DSTStartDay)
WHEN 1 THEN 8
ELSE DATEPART(dw, #DSTStartDay)
END,
#DSTStartDay),
DATEADD(d,
8 - CASE DATEPART(dw, #DSTEndDay)
WHEN 1 THEN 8
ELSE DATEPART(dw, #DSTEndDay)
END,
#DSTEndDay),
-1;
SET #DSTStartDay = DATEADD(yy,1,#DSTStartDay)
INSERT #DST
SELECT DATEADD(d,
8 - CASE DATEPART(dw, #DSTEndDay)
WHEN 1 THEN 8
ELSE DATEPART(dw, #DSTEndDay)
END,
#DSTEndDay),
DATEADD(d,
15 - CASE DATEPART(dw, #DSTStartDay)
WHEN 1 THEN 8
ELSE DATEPART(dw, #DSTStartDay)
END,
#DSTStartDay),
0;
SET #DSTEndDay = DATEADD(yy,1,#DSTEndDay)
SET #i = #i + 1
END
-- Get temporary offset for current date
SET #currDate = GETDATE()
SELECT #Offset = DSTOffset FROM #DST
WHERE StartDate < #currDate AND EndDate >= #currDate
-- Calculate Time Zone Offset (ignore DST) and update DSTOffset in #DST table from temporary to actual offset
SET #Offset = DATEDIFF(hh, GETDATE(), GETUTCDATE()) - #Offset
UPDATE #DST
SET DSTOffset = DSTOffset + #Offset
--SELECT * FROM #DST - look at the table
--Sample usage
DECLARE #myDateNoDST datetime = '2014-03-08 06:00',
#myDateWithDST datetime = '2014-03-09 06:00'
SELECT #myDateNoDST LocalDateNoDST,
(SELECT DATEADD(hh,DSTOffset,#myDateNoDST) FROM #DST d WHERE StartDate < #myDateNoDST AND EndDate >= #myDateNoDST) UTCDateNoDST,
#myDateWithDST LocalDateWithDST,
(SELECT DATEADD(hh,DSTOffset,#myDateWithDST) FROM #DST d WHERE StartDate < #myDateWithDST AND EndDate >= #myDateWithDST) UTCDateWithDST
I need to get the number of weekends between dates in sql as a function. I have tried but stuck up somewhere in the logic.
CREATE FUNCTION fnc_NumberOfWeekEnds(#dFrom DATETIME, #dTo DATETIME)
RETURNS INT AS
BEGIN
Declare #weekends int
Set #weekends = 0
While #dFrom <= #dTo Begin
If ((datepart(dw, #dFrom) = 1))
Set #weekends = #weekends + 1
Set #dFrom = DateAdd(d, 1, #dFrom)
End
Return (#weekends)
END
I tried out this logic with several edge cases and it seems to work.
SELECT DATEDIFF(d, #dFrom, #dTo)/7+1
+ CASE WHEN DATEPART(dw,#dFrom) IN (1,7) THEN -1 ELSE 0 END
+ CASE WHEN DATEPART(dw,#dTo) IN (1,7) THEN -1 ELSE 0 END
You can change the CASE statements depending on how you want to handle cases where the start or end date is in a weekend. In my case I'm not including the weekend if the start or end date is a Saturday or Sunday.
Try replacing the if statement with this:
If ((datepart(dw, #dFrom) = 1) OR (datepart(dw, #dFrom) = 7))
You should also check the end of the week to get the result.
DECLARE #date_from DATETIME,
#date_to DATETIME
/*TEMPORARY TABLE*/
DECLARE #DATES AS TABLE (
GDate DATETIME
)
SELECT #date_from ='2019-09-10'
SELECT #date_to ='2019-10-10'
/*DATE GENERATED*/
WITH dates
AS (
SELECT #date_from AS dt
UNION ALL
SELECT DATEADD(D, 1, dt)
FROM dates
WHERE dt < #date_to
)
/*INSERTED DATES INTO TEMP TABLE */
INSERT INTO #DATES
SELECT CONVERT(DATETIME, dt) AS Gdate FROM dates
/*Get Records from temp table*/
SELECT Gdate FROM #DATES
Used below logic to calculate the no of Saturdays or Sundays between a start date and end date.
CREATE FUNCTION dbo.WEEKEND_COUNT
(
#Start_Date datetime,
#End_Date datetime
)
RETURNS int
AS
BEGIN
Declare #count int = 0;
while #Start_Date<=#End_Date
Begin
IF DatePart(WEEKDAY,#Start_Date) = 1 or DatePart(WEEKDAY,#Start_Date) = 7
SET #count=#count+1
SET #Start_Date=DateAdd(d,1,#Start_Date)
END
return #count
END
--Use below to get the count of Saturdays and Sundays
Select dbo.WEEKEND_COUNT('Your start date','your end date')
This will give you the number of sunday between two dates
SELECT DateDiff(ww, #dFrom, #dTo) as NumOfSundays
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
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