Im stuck in solving something. How can I check if a time range is between another time range?
For example how can I check if 11:00AM - 12:00PM is between 9:30AM - 2PM?
I need this in developing timekeeping system to check if the filed Sick Leave of an employee covers his scheduled break time.
Im using SQL Server 2008
declare #timefrom datetime
declare #timeto datetime
declare #timefromBreak datetime
declare #timetoBreak datetime
set #timefrom = cast('01/21/2015 11:30:00' as datetime)
set #timeto = cast('01/21/2015 14:00:00' as datetime)
set #timefromBreak = cast('01/21/2015 11:00:00' as datetime)
set #timetoBreak = cast('01/21/2015 12:00:00' as datetime)
select
case when ((#timefromBreak between #timefrom and #timeto) AND (#timetoBreak between #timefrom and #timeto)) then
(datediff(hour, #timefrom,#timeto) - datediff(hour,#timefromBreak,#timetoBreak))
else datediff(hour, #timefrom,#timeto)
end as TotalLeaveHours
here is my example.. im trying to get the total leave hours an employee filed for his Leave.
The logic is that i will get the total leave hours not including the break time (if and only if the leave time range covers the break time.)
So if an employee filed a leave for 11:30PM to 2:00PM and his scheduled break time is 11:00Am to 12:00PM. The total leave hours should only be 2Hours.
11:30AM to 12:00PM should not be counted for the total leave hours since his scheduled time is 11:00AM to 12:00PM..
This computes the time difference (in minutes at the moment1) and tries to take care of all possible overlaps:
declare #timefrom datetime
declare #timeto datetime
declare #timefromBreak datetime
declare #timetoBreak datetime
select #timefrom = '2015-01-21T11:30:00',#timeto = '2015-01-21T14:00:00',
#timefromBreak = '2015-01-21T11:00:00',#timetoBreak = '2015-01-21T12:00:00'
select DATEDIFF(minute,#timefrom,#timeto) -
CASE WHEN #timeFrom < #timeToBreak AND #timefromBreak < #timeTo THEN
DATEDIFF(minute,
CASE WHEN #timeFrom > #timeFromBreak THEN #timeFrom ELSE #timeFromBreak END,
CASE WHEN #timeTo < #timeToBreak THEN #timeTo ELSE #timeToBreak END
) ELSE 0 END
Hopefully, you can see the basic logic - we always compute the time difference - and then we check whether an overlap exists. Only if an overlap exists do we need to adjust the total time, and we do that using a couple of CASE expressions to account for when the break time and the leave time partially overlap or entirely overlap.
(If SQL Server had MIN and MAX functions that worked against multiple arguments instead of multiple rows, then the later CASE expressions would have just been MAX(#timeFrom,#timeFromBreak) and MIN(#timeTo,#timeToBreak))
1Since in some of your examples you seem to be working with e.g. 2.5 hours, I though it would be safer to work in minutes and let you perform any final adjustment/rounding down/up as a final step, separate from these calculations
4 is the total leave hours..
Sorry for making confusions..I need to get the total number of hours..I tried it and i think there will be no conflict if the leave time range will cover the entire break time..how ever if only part of the break time will be covered, the first condition will be false..
if the employee's break schedule is 11:00AM to 12:00PM, then the leave he filed is from 11:30AM to 2:00Pm the Total Leave Hours should return only 3 hours, instead of 2.5hours.
in my testing,
declare #timefrom datetime
declare #timeto datetime
declare #timefromBreak datetime
declare #timetoBreak datetime
set #timefrom = cast('01/21/2015 11:30:00' as datetime)
set #timeto = cast('01/21/2015 14:00:00' as datetime)
set #timefromBreak = cast('01/21/2015 11:00:00' as datetime)
set #timetoBreak = cast('01/21/2015 12:00:00' as datetime)
select
case when ((#timefromBreak between #timefrom and #timeto) AND (#timetoBreak between #timefrom and #timeto)) then
(datediff(hour, #timefrom,#timeto) - datediff(hour,#timefromBreak,#timetoBreak))
else datediff(hour, #timefrom,#timeto)
end as TotalLeaveHours
this returns 3 hours because it doesn't meet the first condition in Select Case statement.
Related
I am using a function which currently calculates the SLA between two dates passed in, this checks for NULL items and returns 0.
I now need to take into account the dates inside a new table 'NonBusinessDays' and take these off the total SLA value.
How would I check if the two passed in values contain any dates between their range from the other tables dates, if they do they need to take off that date from the SLA total.
Currently the table sits like;
CREATE TABLE #NonBusinessDays(
[id] [uniqueidentifier] NOT NULL,
[date] [date] NOT NULL,
[name] [nvarchar](100) NOT NULL,
[type] [nvarchar](50) NOT NULL)
GO
INSERT INTO #NonBusinessDays
([id]
,[date]
,[name]
,[type])
VALUES
('1DF6A335-56F8-4DA7-B9C3-07DBAC9FBA8D'
,'2019-08-26'
,'Summer bank holiday'
,'PublicHoliday')
('0940FDC9-A875-48C5-BED8-0ABFA3AA5AE8',
'2018-05-07',
'Early May bank holiday',
'PublicHoliday')
GO
So my Function is like this;
CREATE FUNCTION dateSLA
(
#date1 DATETIME,
#date2 DATETIME
)
returns INT
AS
BEGIN
declare #days INT
set #days = 0
IF #date1 IS NULL AND #date2 IS NULL
BEGIN
GOTO Final
END
ELSE IF #date1 IS NOT NULL AND #date2 IS NOT NULL
BEGIN
SET #days = DATEDIFF(DAY, CONVERT(DATE, #date1, 103), CONVERT(DATE, #date2, 103))
GOTO Final
END
Final:
return #days
END
GO
I would like it to check if any dates inside the NonBusinessDays Table are present between the parameters passed into the Function then it would take however many days off the SLA.
Example, if two dates from the BusinessDays exist between the passed in the parameters then this would take 2 off. So the SLA calcualted at 10 but this would make it 8 due to 2 non-business days were present.
Any guidance would be appreciated, if you require any more information please let me know and I will amend.
We can reduce this function considerably and take into account the non working days:
CREATE FUNCTION dateSLA
(
#date1 DATETIME,
#date2 DATETIME
)
returns INT
AS
BEGIN
return COALESCE(
DATEDIFF(day,#date1,#date2) -
(select COUNT(*) from #NonBusinessDays
where date between CAST(#date1 as date) and CAST(#date2 as date)),
0)
END
GO
If either #date1 or #date2 is null then the datediff will return null, the subtraction will leave null and finally the coalesce turns this into a 0.
DATEDIFF counts boundaries There are just as many day boundaries (transitions at midnight) between two datetimes and the same values with the time portion removed. So we don't need them as dates for the DATEDIFF call. We do need such for matching a non-working day on the start date, so we retain the CAST there, and also because of the data type mismatch and for symmetry we do the same for the end date.
I'm ignoring what happens when #date2 is less than #date1, which is presumably an invalid input to this function anyway.
As I said in a comment, I'd prefer a full calendar table here where we could just do the COUNT(*) operation against the working days, but understand it's not an option at this time for you. (At which point you start questioning the need for a function at all)
I want to get date from yyyy-mm-dd to yyyy-mm-dd in SQL.
Example: I have two parameter #startdate : 2015-12-28 and #enddate : 2016-01-02, and database in SQLServer, datatype is varchar(10)
DATE_ORDER
28-12-2015
30-12-1996
29-12-2016
30-12-1997
24-12-2015
27-12-1993
03-01-2016
01-01-1992
02-01-2016
etc...
Ok,now I want to get data from #startdate : 2015-12-28 and #enddate : 2016-01-02. I use SELECT * FROM TABLE_X WHERE DATE_ORDER >= #startdate AND DATE_ORDER <= #enddate . But the results are not what I expected. Here are the results I want
28-12-2015
30-12-1996
29-12-2016
30-12-1997
01-01-1992
02-01-2016
I think to solve this problem, I need to do two things :
First, get date range from #startdate to #enddate , in here 28/12/2015, 29/12/2015, 30/12/2015, 31/12/2015, 01/01/2016, 02/01/2016.
The second: get the date in database same in range 28/12, 29/12, 30/12, 31/12, 01/01, 02/01, ignoring the year.
Can you give me some ideas about this ?
Your actual format is "105-italian" find details here.
You can convert your existing VARCHAR(10)-values with this line to real datetime
SELECT CONVERT(DATETIME,YourColumn,105)
Next thing to know is, that you should not use BETWEEN but rather >=StartDate AND < NakedDateOfTheFollowingDay to check date ranges
So to solve your need Get date-range from 2015-12-28 to 2016-01-02 you might do something like this:
DECLARE #Start DATETIME={d'2015-12-28'};
DECLARE #End DATETIME={d'2016-01-02'};
SELECT *
FROM YourTable
WHERE CONVERT(DATETIME,YourDateColumn,105)>=#Start AND CONVERT(DATETIME,YourDateColumn,105)<#End+1
Attention Be aware, that the conversion lets your expression be not sargable. No index will be used.
Better was to store your date as correctly typed data to avoid conversions...
Try this query
SET DATEFIRST 1
DECLARE #wk int SET #wk = 2
DECLARE #yr int SET #yr = 2011
--define start and end limits
DECLARE #todate datetime, #fromdate datetime
SELECT #fromdate = dateadd (week, #wk, dateadd (YEAR, #yr-1900, 0)) - 4 -
datepart(dw, dateadd (week, #wk, dateadd (YEAR, #yr-1900, 0)) - 4) + 1
SELECT #todate = #fromdate + 6
;WITH DateSequence( Date ) AS
(
SELECT #fromdate AS Date
UNION ALL
SELECT dateadd(DAY, 1, Date)
FROM DateSequence
WHERE Date < #todate
)
--select result
SELECT * FROM DateSequence OPTION (MaxRecursion 1000)
So, after the 2nd or 3rd edit, it slowly becomes clear, what you want (i hope).
So you REALLY WANT to get the dates with the year beeing ignored.
As someone pointed out already, date-values are stored internally not as string, but as internal datatype date (whatever that is in memory, i don't know).
If you want to compare DATES, you cannot do that with ignorance of any part. If you want to, you have to build a NEW date value of day and month of given row and a hard coded year (2000 or 1 or whatever) for EVERY row.
SELECT * FROM TABLE_X WHERE convert(date,'2000' + substring(convert(char(8),convert(datetime, 'DATE_ORDER', 105),112),5,4),112) >= #startdate AND convert(date,'2000' + substring(convert(char(8),convert(datetime, 'DATE_ORDER', 105),112),5,4),112) <= #enddate
If your startdate and enddate go OVER sylvester, you have to do 2 queries, on from startdate to 1231, one from 0101 to enddate.
I need to get a random date-time within a range of 2 date-times in T-SQL for MS-SQL server 2012. It is necessary for me that it goes to the precision of seconds, I need DATETIME2(0) as datatype for both input and output. I have done some research online but the answers I could find are either for dates only or are for other database types.
The code should do something like:
Input: ('2015-01-01 08:22:13' , '2015-03-05 17:56:31') <-- I will use a select statement to replace these absolute numbers
Return: ('2015-02-11 14:32:45') <--this should be randomized
Can anyone help me out with this?
Thanks in advance
One option is to randomize the numbers of seconds between FromDate and ToDate, and then add it to FromDate. Hope it works for you.
DECLARE #FromDate DATETIME2(0)
DECLARE #ToDate DATETIME2(0)
SET #FromDate = '2015-01-01 08:22:13'
SET #ToDate = '2015-03-05 17:56:31'
DECLARE #Seconds INT = DATEDIFF(SECOND, #FromDate, #ToDate)
DECLARE #Random INT = ROUND(((#Seconds-1) * RAND()), 0)
SELECT DATEADD(SECOND, #Random, #FromDate)
ALTER PROCEDURE [dbo].[spInsert]
(#PlanName Varchar(50)=null
,#StartDate Datetime
,#EndDate Datetime
,#ModifiedBy Varchar(100)=null
,#ReturnValue Int Out)
As
BEGIN
IF NOT EXISTS(SELECT PlanName FROM dbo.tblPlan WHERE PlanName=#PlanName)
BEGIN
IF((SELECT COUNT(*) FROM tblPlan WHERE StartDate <= #StartDate AND EndDate <=
#EndDate)<0)
BEGIN
INSERT INTO dbo.tblPlan VALUES(3,#PlanName,#StartDate,#EndDate,#ModifiedBy,GETDATE(),
(SELECT DATEDIFF(DD,#StartDate,#EndDate)))
SET #ReturnValue=1;
END
ELSE
SET #ReturnValue=-2;
END
ELSE
SET #ReturnValue=-1;
END
I am trying to achieve the below thing.I want to check user supplied startDate and Enddate is in between the existing table startdate and enddate. if any of the date of user supplied date range is in between the tables start date and end date,it should return -2,if the record does not exists it should insert the details..
I Could not achieve this logic.where i went wrong ..please suggest me any solution to this.
EDIT:First Consditon check whether planName is exists or not,If not exists then want to check Start and End Date already existed or Not(Including start and End)
I tried two ways as suggested mentioned in the replies.
eg:If existing start and end range is Start-2013-10-09,End-2013-10-15,If am going to insert another plan then the start and end date of this plan should not fall between 9th-15th october and start and End date should not be 9th or 15 th both.
ONE:IF((SELECT COUNT(*) FROM tblPlan WHERE StartDate <= #StartDate AND EndDate <=
#EndDate)=0)
Result: It does not insert any data, even it is out of previous date. or with in the
range
SECOND:IF((SELECT COUNT(*) FROM tblPlan WHERE StartDate>=#StartDate AND
EndDate<=#EndDate)=0)
RESULT: It insert the date with out Considering the above condition.
I think you need to change your if from
IF((SELECT COUNT(*) FROM tblPlan WHERE StartDate <= #StartDate AND EndDate <= #EndDate)<0)
to
IF((SELECT COUNT(*) FROM tblPlan WHERE StartDate <= #StartDate AND EndDate >= #EndDate)=0)
Which should ensure that #StartDate and #EndDate is between StartDate and EndDate and test for =0
COUNT(*) can never be smaller than zero like your code suggests.
It's either a positive integer (which is greater than zero), or null, which will also return false on any arithmetic condition.
try the below :
ALTER PROCEDURE [dbo].[spInsert]
(#PlanName Varchar(50)=null
,#StartDate Datetime
,#EndDate Datetime
,#ModifiedBy Varchar(100)=null
,#ReturnValue Int Out)
As
BEGIN
IF NOT EXISTS(SELECT PlanName FROM dbo.tblPlan WHERE PlanName=#PlanName)
BEGIN
IF((SELECT COUNT(*) FROM tblPlan WHERE StartDate >= #StartDate AND EndDate <=
#EndDate)=0)
BEGIN INSERT INTO dbo.tblPlan
VALUES(3,#PlanName,#StartDate,#EndDate,#ModifiedBy,GETDATE(),
(SELECT DATEDIFF(DD,#StartDate,#EndDate)))
SET #ReturnValue=1;
END
ELSE
SET #ReturnValue=-2;
END
ELSE
SET #ReturnValue=-1;
END
As count always return positive value or zero. so your first condition always being false.
*UPDATE:*
you want to say if Sdate is '12-12-2013' and edate is '15-12-2013' then you don't want to considers these dates in check if so then try replace the query with below:
IF((SELECT COUNT(*) FROM tblPlan WHERE StartDate > #StartDate AND EndDate <
#EndDate)=0)
If you're wanting to check whether the period defined by #StartDate,#EndDate overlaps with any period defined by the values in the StartDate,EndDate columns for a particular row, then the actual comparison that you want to perform is:
StartDate < #EndDate AND EndDate < #StartDate
(With appropriate adjustments of < to <= depending on whether you want to consider two periods such that one starts at the exact time that the other finishes as overlapping or not)
The logic is - Two periods overlap if both of the following conditions are true:
period 1 starts before period 2 ends
period 2 starts before period 1 ends
Other notes -
A COUNT will never be <0 so that part of the logic is incorrect. If you merely want to determine whether any rows exists, use EXISTS (as you already have once) - don't force a COUNT if you don't actually need to know how many rows match your criteria.
I also find it slightly suspect that your first query considers PlanName but your second doesn't. Are you sure that this is correct?
I need to calculate difference in workdays between two dates. Is there a built in function for this in SQL Server? Can someone please provide an example on how to do this?
Here is something I wrote quickly. Just encapsulate it into a function or whatever you need.
declare #StartDate datetime
declare #EndDate datetime
declare #TotalDiff int
declare #NumberOfWeekends int
SET #StartDate = '3/12/2013'
SET #EndDate = '3/22/2013'
SET #NumberOfWeekends = 0
SET #TotalDiff = DATEDIFF(d,#StartDate, #EndDate)
If #TotalDiff > 7
SET #NumberOfWeekends = #TotalDiff / 7
else if DATEPART(dd, #EndDate) < DATEPART(DD, #StartDate)
SET #NumberOfWeekends = 1
select (#TotalDiff - 2*#NumberOfWeekends) as TotalWorkDays
No, there is nothing built in to SQL Server to directly give you number of working days between two dates, however there are a few built-in functions which will enable you to write one.
Firstly, a few caveats
The world cannot agree what a "Working Day" is. For most of us it's Saturday and Sunday. For most of the Middle East it's Friday & Saturday (with Sunday being a normal working day)
The world most certainly cannot agree on what constitutes a public holiday, which are almost always considered non-working days.
You have not specified how you would like to handle these cases so lets make some assumptions:
Saturday and Sunday will be non-working days
Public holidays will not be taken into acount
Now, determining if a particular days is saturday or sunday in sql is easy, given a #date of type DateTime:
IF DATENAME(dw,#date) IN ('Saturday','Sunday')
With that in mind, given a start and end date, you can just count incrementally from #startDate to #endDate
DECLARE #startDate DATETIME = '2013-01-01'
DECLARE #endDate DATETIME = '2013-01-20'
DECLARE #currDate DATETIME = #startDate
DECLARE #numDays INT = 0
WHILE #currDate<#endDate
BEGIN
IF DATENAME(dw,#currDate) NOT IN ('Saturday','Sunday')
SET #numDays = #numDays + 1
SET #currDate = DATEADD(day,1,#currDate)
END
SELECT #numDays
Note: This is non-inclsive so wont count #endDate. You could change it to be inclusive by changing WHILE #currDate<#endDate to WHILE #currDate<=#endDate
My solution does not count the #EndDate, so if you need to change that, just add 1 to #d2.
First, I calculate the number of days from an "initial" day (which happens to be 1/1/1900, a Monday) to #StartDate and #EndDate:
DECLARE #d1 int = DATEDIFF(Day, 0, #StartDate);
DECLARE #d2 int = DATEDIFF(Day, 0, #EndDate);
Then, the total number of days between #StartDate and #EndDate is:
#d2 - #d1
From this, I substract the number of Sundays and the number of Saturdays in the interval, each calculated as a difference simlar to the total days, but now for whole weeks (7 days). To get the number of whole weeks, I use integer division by 7 and the fact that the "initial" day (0) is a Monday. The number of Sundays in the interval is
#d2/7 - #d1/7
and the number of Saturdays is
(#d2+1)/7 - (#d1+1)/7
Putting all together, my solution is:
DECLARE #StartDate DATETIME = '20180101'
DECLARE #EndDate DATETIME = '20180201'
DECLARE #d1 int = DATEDIFF(Day, 0, #StartDate)
DECLARE #d2 int = DATEDIFF(Day, 0, #EndDate)
SELECT #d2 - #d1 - (#d2/7 - #d1/7) - ((#d2+1)/7 - (#d1+1)/7) AS workdays