How to get the difference between two times with SQL Server? - sql-server

I have the following two column with Time values
;WITH cte AS (
SELECT *
FROM (VALUES
('08:00:00','18:30:00'),
('20:00:00','08:00:00'),
('18:30:00','06:00:00'),
('08:00:00','18:30:00')) as t(time1, time2)
)
How can I get the following difference values:
result
10.5
12
11.5
10.5
I tried with DATEDIFF but I am never able to get a true values especially for the case from 18:30 to 6:00
I tried like this, but does not work for this case(8:30 to 6:00) ...
CASE
WHEN GirisSaati > CikisSaati THEN cast( DATEDIFF(MINUTE, cast(CikisSaati as time ), cast(GirisSaati as time ))as float) / 60
WHEN GirisSaati <= CikisSaati THEN cast( DATEDIFF(MINUTE,cast( GirisSaati as time ), cast(CikisSaati as time ) )as float) /60
END
Hope to find help, thanks ...

This is because you are dividing by 60, an integer. This will return an integer result. Instead try dividing by a numeric:
Example
DECLARE #Time1 TIME(0) = '08:00:00';
DECLARE #Time2 TIME(0) = '18:30:00';
SELECT
DATEDIFF(MINUTE, #Time1, #Time2) / 60 AS Incorrect,
DATEDIFF(MINUTE, #Time1, #Time2) / 60.0 AS Correct
;
Result
Incorrect Correct
10 10.500000
This is a consequence of data type precedence. From MSDN:
When an operator combines two expressions of different data types, the
rules for data type precedence specify that the data type with the
lower precedence is converted to the data type with the higher
precedence. If the conversion is not a supported implicit conversion,
an error is returned. When both operand expressions have the same data
type, the result of the operation has that data type.
In this case INT has the highest precedence.
Alternatively you could use CAST to explicitly set the data type: CAST(60 AS DECIMAL(18,2)).

Here is how you can do it:
DECLARE #t table(time1 time, time2 time)
INSERT #t values
('08:00','18:30'),
('20:00','08:00'),
('18:30','06:00'),
('08:00','18:30')
;WITH CTE as
(
SELECT
time1, time2,
CASE WHEN time2 < time1 THEN 1 ELSE 0 END
+ CAST(time2 as datetime) - CAST(time1 as datetime) diff
FROM #t
)
SELECT
time1,
time2,
cast(diff as time) timediff,
cast(diff as float) * 24 decimaldiff
FROM CTE
Result:
time1 time2 timediff decimaldiff
08:00 18:30 10:30 10.5
20:00 08:00 12:00 12
18:30 06:00 11:30 11.5
08:00 18:30 10:30 10.5

I found another option...
Step 1 -> Create this SQL function
CREATE FUNCTION [dbo].[get_TimeDefferance_hh_mm_ss]
(
-- Add the parameters for the function here
#FromTime time,#ToTime time
)
RETURNS time
AS
BEGIN
declare #totalsec decimal(10,2)= datediff(SECOND,#FromTime,#ToTime);
declare #secondPart int= #totalsec%60
--select #secondPart As secondpart
declare #totalminsOnly int= (#totalsec-#secondPart)/60
declare #MinsPart int=#totalminsOnly%60
--select #MinsPart AS minsPart
declare #totalHoursOnly int= (#totalminsOnly-#MinsPart)/60
--select #totalHoursOnly AS totalhoursonly
-- Return the result of the function
RETURN cast(#totalHoursOnly AS varchar(50))+':'+cast(#MinsPart as varchar(50))+':'+cast(#secondPart as varchar(50))
END
Step 2->Go to new query and check is it working..
declare #FromTime time='2:30:41';
declare #ToTime time='4:20:30';
declare #timeDiff time= **dbo.get_TimeDefferance_hh_mm_ss(#FromTime,#ToTime)**
select #timeDiff
output---
01:49:49.0000000

Maybe it is a 'bit' later, but I will post my solution in order to help someone else.
set #from = '10:15';
set #to = '12:30';
select (time_to_sec(timediff(#to, #from))/3600);
Output is 2.2500

Related

calculate avg time in MS SQL

i have a table with datetime, i need to find the avg of the time. i tried CASE statement but it gave me an error Operand data type time is invalid for avg operator.
select * from datestime
dates
2015-11-23 15:05:40.923
2015-11-23 15:05:43.610
2015-11-23 15:05:45.790
2015-11-23 15:05:48.293
first I split the colum into two column date and time
"select convert(date,dates,104) as date,convert(time,dates,108) as time from datestime"
then use CASE To calculate the avg of time.
;with avgtime as(
select convert(date,dates,104) as date,convert(time,dates,108) as time from datestime)
select avg(time) from avgtime
please help me to find the avg time
You can get the midpoint between the start/end date in the range by doing this:
declare #start datetime = '2015-12-01T10:00:00'
declare #finish datetime = '2015-12-01T11:00:00'
print dateadd( second, ( datediff( second, #start, #finish ) / 2 ), #start )

What is the most accurate way of using DATEDIFF in SQL Server?

I have two computed columns (MonthsInService and YearsInService) with the following expressions.
MonthsInService = (datediff(month,[DateEngaged],getdate()))
YearsInService = (datediff(month,[DateEngaged],getdate())/(12))
Now if for example DateEngaged = 2012-April-09 and getdate() is 2013-April-08, MonthsInService returns 12 and YearsInService is 1.
My application requires that YearsInService be Zero since there is still one day to go before the employees first Anniversary.
Am not even sure how to best handle the MonthsInService column since months have varying number of days.
Unfortunately, DATEDIFF computes the number of transitions of the element, rather than the usual, human intuition of the difference between two dates (e.g. DATEDIFF(year,'20121231','20130101') is 1, even though not many people would say that there's a difference of a year).
The solution I'd use is a bit repetitive, but doesn't need a separate function, and always gets e.g. leap years correct:
declare #T table (
DateEngaged datetime not null,
MonthsInService as CASE
WHEN DATEADD(month,DATEDIFF(month,DateEngaged,GETDATE()),DateEngaged) > GETDATE()
THEN DATEDIFF(month,DateEngaged,GETDATE()) - 1
ELSE DATEDIFF(month,DateEngaged,GETDATE())
END,
YearsInService as CASE
WHEN DATEADD(year,DATEDIFF(year,DateEngaged,GETDATE()),DateEngaged) > GETDATE()
THEN DATEDIFF(year,DateEngaged,GETDATE()) - 1
ELSE DATEDIFF(year,DateEngaged,GETDATE())
END
)
insert into #T (DateEngaged) values ('20120409'),('20120408')
select * from #T
Produces:
DateEngaged MonthsInService YearsInService
----------------------- --------------- --------------
2012-04-09 00:00:00.000 11 0
2012-04-08 00:00:00.000 12 1
It works by asking "If we take the naive answer produced by DATEDIFF, does it given an answer that's too high by 1?" - and if so, we just subtract one from the answer it gives. DATEDIFF should only ever be over by 1.
Via using day you can reach the result:
select
datediff(month,'2012-April-09','2013-April-08') MonthsInService
,datediff(day,'2012-April-09','2013-April-08')/365 YearsInService
Output:
12 0
or use function for maximum precision:
CREATE FUNCTION [dbo].[getFullYears]
(
#dateX datetime,
#dateY datetime
)
RETURNS int
AS
BEGIN
DECLARE #y int
SET #y =DATEDIFF(year,#dateX,#dateY)
IF (#dateY < DATEADD(year, #y, #dateX)) SET #y = #y -1
RETURN #y
END
select dbo.getFullYears('2012-April-09','2013-April-09') --1
select dbo.getFullYears('2012-April-09','2013-April-08') --0
For months calculation you can refer here: Calculating number of full months between two dates in SQL
Try this query :
DATEDIFF(DAY, CONVERT(date, dtmDOB),
CONVERT(date, GETDATE()))*(12.0/365.25)),1))
AS TotalMonths,

Datepart for time between (instead of Convert date)

Is there a way to use Datepart to select rows which have time between like 12:20 and 15:50 using datepart, because Convert date to time is unusably slow for me?
Just for example you can use this
DECLARE
#min FLOAT = CAST(CAST('19000101 12:20' AS DATETIME) AS FLOAT),
#max FLOAT = CAST(CAST('19000101 15:50' AS DATETIME) AS FLOAT)
SELECT
*
FROM table
WHERE CAST(DateField AS FLOAT) - FLOOR(CAST(DATEFIELD AS FLOAT)) BETWEEN #min AND #max
But this is actually not a solution!!!
The best way is to introduce 1 more calculated column as
NewColumn AS DATEPART(HOUR, DateColumn)*100+DATEPART(minute, DateColumn)
Create index on it and use in where clause
WHERE NewColumn BETWEEN 1220 AND 1550
Assuming time periods on the same day;
...
where cast(fld as time) between '12:20' and '15:50'
The only alternative is t use a CTE. Tested it and it works.
LH = Low hour
HH - High Hour
LM - Low minute
HM = High minute
;WITH CTE_LH AS
(
SELECT *
FROM DateTable
WHERE (DATEPART(HOUR, DateCol) >= 12)
)
, CTE_HH AS
(
SELECT *
FROM CTE_LH
WHERE (DATEPART(HOUR,DateCol) <= 15 )
)
,CTE_LM AS
(
SELECT *
FROM CTE_HH
WHERE (DATEPART(MINUTE,DateCol) >= 20 )
)
,CTE_HM AS
(
SELECT *
FROM CTE_LM
WHERE (DATEPART(MINUTE,DateCol) <= 50 )
)
SELECT * FROM CTE_HM;

SQL Server remove milliseconds from datetime

select *
from table
where date > '2010-07-20 03:21:52'
which I would expect to not give me any results... EXCEPT I'm getting a record with a datetime of 2010-07-20 03:21:52.577
how can I make the query ignore milliseconds?
You just have to figure out the millisecond part of the date and subtract it out before comparison, like this:
select *
from table
where DATEADD(ms, -DATEPART(ms, date), date) > '2010-07-20 03:21:52'
If you are using SQL Server (starting with 2008), choose one of this:
CONVERT(DATETIME2(0), YourDateField)
LEFT(RTRIM(CONVERT(DATETIMEOFFSET, YourDateField)), 19)
CONVERT(DATETIMEOFFSET(0), YourDateField) -- with the addition of a time zone offset
Try:
SELECT *
FROM table
WHERE datetime >
CONVERT(DATETIME,
CONVERT(VARCHAR(20),
CONVERT(DATETIME, '2010-07-20 03:21:52'), 120))
Or if your date is an actual datetime value:
DECLARE #date DATETIME
SET #date = GETDATE()
SELECT CONVERT(DATETIME, CONVERT(VARCHAR(20), #date, 120))
The conversion to style 120 cuts off the milliseconds...
select * from table
where DATEADD(ms, DATEDIFF(ms, '20000101', date), '20000101') > '2010-07-20 03:21:52'
You'll have to trim milliseconds before comparison, which will be slow over many rows
Do one of these to fix this:
created a computed column with the expressions above to compare against
remove milliseconds on insert/update to avoid the read overhead
If SQL Server 2008, use datetime2(0)
Use CAST with following parameters:
Date
select Cast('2017-10-11 14:38:50.540' as date)
Output: 2017-10-11
Datetime
select Cast('2017-10-11 14:38:50.540' as datetime)
Output: 2017-10-11 14:38:50.540
SmallDatetime
select Cast('2017-10-11 14:38:50.540' as smalldatetime)
Output: 2017-10-11 14:39:00
Note this method rounds to whole minutes (so you lose the seconds as well as the milliseconds)
DatetimeOffset
select Cast('2017-10-11 14:38:50.540' as datetimeoffset)
Output: 2017-10-11 14:38:50.5400000 +00:00
Datetime2
select Cast('2017-10-11 14:38:50.540' as datetime2)
Output: 2017-10-11 14:38:50.5400000
For this particular query, why make expensive function calls for each row when you could just ask for values starting at the next higher second:
select *
from table
where date >= '2010-07-20 03:21:53'
Use 'Smalldatetime' data type
select convert(smalldatetime, getdate())
will fetch
2015-01-08 15:27:00
There's more than one way to do it:
select 1 where datediff(second, '2010-07-20 03:21:52', '2010-07-20 03:21:52.577') >= 0
or
select *
from table
where datediff(second, '2010-07-20 03:21:52', date) >= 0
one less function call, but you have to be beware of overflowing the max integer if the dates are too far apart.
One more way I've set up SQL Server queries to ignore milliseconds when I'm looking for events from a particular second (in a parameter in "YYYY-MM-DD HH:TT:SS" format) using a stored procedure:
WHERE
...[Time_stamp] >= CAST(CONCAT(#YYYYMMDDHHTTSS,'.000') as DateTime) AND
...[Time_stamp] <= CAST(CONCAT(#YYYYMMDDHHTTSS,'.999') as DateTime)
You could use something similar to ignore minutes and seconds too.
Please try this
select substring('12:20:19.8470000',1,(CHARINDEX('.','12:20:19.8470000',1)-1))
(No column name)
12:20:19
I'm very late but I had the same issue a few days ago. None of the solutions above worked or seemed fit. I just needed a timestamp without milliseconds so I converted to a string using Date_Format and then back to a date with Str_To_Date:
STR_TO_DATE(DATE_FORMAT(your-timestamp-here, '%Y-%m-%d %H:%i:%s'),'%Y-%m-%d %H:%i:%s')
Its a little messy but works like a charm.
May be this will help..
SELECT [Datetime] = CAST('20120228' AS smalldatetime)
o/p:
2012-02-28 00:00:00
Review this example:
declare #now datetimeoffset = sysdatetimeoffset();
select #now;
-- 1
select convert(datetimeoffset(0), #now, 120);
-- 2
select convert(datetimeoffset, convert(varchar, #now, 120));
which yields output like the following:
2021-07-30 09:21:37.7000000 +00:00
-- 1
2021-07-30 09:21:38 +00:00
-- 2
2021-07-30 09:21:37.0000000 +00:00
Note that for (1), the result is rounded (up in this case), while for (2) it is truncated.
Therefore, if you want to truncate the milliseconds off a date(time)-type value as per the question, you must use:
declare #myDateTimeValue = <date-time-value>
select cast(convert(varchar, #myDateValue, 120) as <same-type-as-#myDateTimeValue>);

How to calculate difference in hours (decimal) between two dates in SQL Server?

I have to calculate the difference in hours (decimal type) between two dates in SQL Server 2008.
I couldn't find any useful technique to convert datetime to decimal with 'CONVERT' on MSDN.
Can anybody help me with that?
UPDATE:
To be clear, I need the fractional part as well (thus decimal type). So from 9:00 to 10:30 it should return me 1.5.
DATEDIFF(hour, start_date, end_date) will give you the number of hour boundaries crossed between start_date and end_date.
If you need the number of fractional hours, you can use DATEDIFF at a higher resolution and divide the result:
DATEDIFF(second, start_date, end_date) / 3600.0
The documentation for DATEDIFF is available on MSDN:
http://msdn.microsoft.com/en-us/library/ms189794%28SQL.105%29.aspx
Just subtract the two datetime values and multiply by 24:
Select Cast((#DateTime2 - #DateTime1) as Float) * 24.0
a test script might be:
Declare #Dt1 dateTime Set #Dt1 = '12 Jan 2009 11:34:12'
Declare #Dt2 dateTime Set #Dt2 = getdate()
Select Cast((#Dt2 - #Dt1) as Float) * 24.0
This works because all datetimes are stored internally as a pair of integers, the first integer is the number of days since 1 Jan 1900, and the second integer (representing the time) is the number of (1) ticks since Midnight. (For SmallDatetimes the time portion integer is the number of minutes since midnight). Any arithmetic done on the values uses the time portion as a fraction of a day. 6am = 0.25, noon = 0.5, etc... See MSDN link here for more details.
So Cast((#Dt2 - #Dt1) as Float) gives you total days between two datetimes. Multiply by 24 to convert to hours. If you need total minutes, Multiple by Minutes per day (24 * 60 = 1440) instead of 24...
NOTE 1: This is not the same as a dotNet or javaScript tick - this tick is about 3.33 milliseconds.
DATEDIFF but note it returns an integer so if you need fractions of hours use something like this:-
CAST(DATEDIFF(ss, startDate, endDate) AS decimal(precision, scale)) / 3600
Using Postgres I had issues with DATEDIFF, but had success with this:
DATE_PART('day',(delivery_time)::timestamp - (placed_time)::timestamp) * 24 +
DATE_PART('hour',(delivery_time)::timestamp - (placed_time)::timestamp) +
DATE_PART('minute',(delivery_time)::timestamp - (placed_time)::timestamp) / 60
which gave me an output like "14.3"
You are probably looking for the DATEDIFF function.
DATEDIFF ( datepart , startdate , enddate )
Where you code might look like this:
DATEDIFF ( hh , startdate , enddate )
DATEDIFF(minute,startdate,enddate)/60.0)
Or use this for 2 decimal places:
CAST(DATEDIFF(minute,startdate,enddate)/60.0 as decimal(18,2))
Declare #date1 datetime
Declare #date2 datetime
Set #date1 = '11/20/2009 11:00:00 AM'
Set #date2 = '11/20/2009 12:00:00 PM'
Select Cast(DateDiff(hh, #date1, #date2) as decimal(3,2)) as HoursApart
Result = 1.00
SELECT DATEDIFF(hh, firstDate, secondDate)
FROM tableName
WHERE ...

Resources