Query Datediff to output HH:MM in SQL Server - sql

I'm working with SQL Server 2012, and I have two columns start and end with varchar(5) values in HH:MM format.
The data looks like this
ID Start End
------------------------
1 00:00 06:00
2 06:00 16:00
3 16:00 18:00
4 18:00 24:00
My query is like this:
SELECT
a.start,
a.[end],
RIGHT('0' + CONVERT(VARCHAR(3), DATEDIFF(MINUTE, a.Start, a.[end]) / 60), 2) + ':' +
RIGHT('0' + CONVERT(VARCHAR(2), DATEDIFF(MINUTE, a.Start, a.[end]) % 60), 2) AS TotalHours
FROM
TransactionActivity a
I exec the query with where clause based on ID number, it gives me the correct result, until in ID 4: i got error like this
Conversion failed when converting date and/or time from character string.
I think it because the End time value is 24:00, how can I make it to get the time difference?

I think you'd be better off converting them into full datetime's (using an arbitrary date) because then the date functions will work correctly.
select
a.[start]
, a.[end]
, RIGHT('0' + CONVERT(varchar(3),DATEDIFF(minute,a.[Start], a.[end])/60),2) + ':' +
RIGHT('0' + CONVERT(varchar(2),DATEDIFF(minute,a.[Start],a.[end])%60),2)
as TotalHours
from (
select id
, case when [Start] = '24:00' then dateadd(minute, 1, convert(datetime, '23:59')) else convert(datetime, [Start]) end [Start]
, case when [End] = '24:00' then dateadd(minute, 1, convert(datetime, '23:59')) else convert(datetime, [End]) end [End]
from TransactionActivity
) a

Related

Sum time in SQL Stored procedure

How sum time in SQL my procedure return:
START -- FINISH --- TOTAL
18:14:47 20:32:54 02:18:06
12:35:45 15:06:06 02:30:21
18:08:26 21:25:39 03:17:12
I need sum column TOTAL (08:05:40)
My stored procedure:
Alter PROCEDURE [dbo].[timeTotal]
#userName nvarchar(50)
AS
BEGIN
SELECT
CONVERT(VARCHAR(8),dateadd(HH,7,workStart),108) AS workStart,
CONVERT(VARCHAR(8),dateadd(HH,7,workFinish),108) AS workFinish,
CONVERT(VARCHAR(8),workFinish - workStart,108) AS total
FROM workTime
WHERE userName = userName
END
I need get 08:05:40
To sum time difference in hours, use the following:
SELECT
(RIGHT('00' + CONVERT(VARCHAR(10), SUM(DATEDIFF(MINUTE, FromTime, ToTime)) / 60), 2)
+ ':' +
RIGHT('00' + CONVERT(VARCHAR(2), SUM(DATEDIFF(Minute, FromTime, ToTime)) % 60), 2)
+ ':' +
RIGHT('00' + CONVERT(VARCHAR(2), SUM(DATEDIFF(SECOND, FromTime, ToTime)) % 60), 2))
AS TotalTime FROM AllocateRoom
Use the above in the stored procedure and it returns sum of time difference as follows:
01:20:20
Finally your expected data as follows:
;WITH DEMO(ID, FromTime, ToTime) AS
(
SELECT TOP 1
ID
,CONVERT(DATETIME,FromTime,120) AS FromTime
,CONVERT(DATETIME,ToTime,120) AS ToTime
FROM (VALUES (1,'2016-09-03 18:14:47.000','2016-09-03 20:32:54.000')
,(2,'2016-09-03 12:35:45.000','2016-09-03 15:06:06.000')
) AS X(ID,FromTime,ToTime)
)
SELECT
k.ID,
k.FromTime,
k.ToTime,
DATEADD(SECOND,DATEDIFF(SECOND, k.FromTime, k.ToTime),CONVERT(TIME(0),'00:00:00',0)) AS TIME_DIFF,
DATEADD(SECOND,SUM(DATEDIFF(SECOND, k.FromTime, k.ToTime)) OVER
(
PARTITION BY (SELECT NULL)
),CONVERT(TIME(0),'00:00:00',0)) AS TIME_SUM
FROM DEMO k;
use DATEDIFF() instead of subtraction to find the time different between 2 dates
DATEDIFF(SECOND, workStart, workFinish) AS total
to display it in HH:MM:SS,
CONVERT(VARCHAR(8), DATEADD(SECOND, 0, DATEDIFF(SECOND, workStart, workFinish)),108) AS total

CAST Correct VARCHAR to DateTime

Good Day
I am working of a existing SQL Server Database. What the developers did is to keep the Date and time separate. The Date is in DateTime format (what I want) but the time is incorrect. if it is 14:30 it shows as 1430 when its 09:25 shows as 925. I am trying tyo combine the date and time to have a Date Time view for an program I am writing on top of this database.
I have created the date as a normal date like this:
CASE
WHEN LEN(T0.BeginTime) = 3 THEN '0' + LEFT(T0.BeginTime, 1) + ':' + RIGHT(T0.BeginTime, 2)
ELSE LEFT(T0.BeginTime, 2) + ':' + RIGHT(T0.BeginTime, 2)
END AS 'NEW Start Time'`
The date now looks like it's the correct format but when I want to combine the date and time I get VARCHAR to DateTime error.
How can I fix this?
This is the error:
The conversion of a varchar data type to a datetime data type resulted in an out-of-range value (ONLY RAN 804 RECORDS)
Thanks
This should do the trick, Hope it helps.
DECLARE #DateTime TABLE (
DateWithTime DATE,
BeginTime INT);
INSERT INTO #DateTime
VALUES ('2014-08-04', '1525'),
('2014-08-04', '525'),
('2014-08-04', '15'),
('2014-08-04', '5'),
('2014-08-04', '0'),
('2014-08-04', '90')
;WITH cte_BeginTimeFix
AS (
SELECT
CONVERT(VARCHAR(10), DateWithTime, 120) AS DateWithTime,
RIGHT('0000' + CAST(BeginTime AS VARCHAR(4)), 4) AS BeginTime
FROM #DateTime
)
, cte_DateString
AS (
SELECT DateWithTime,
BeginTime,
DateWithTime + ' ' + STUFF(STUFF('00:00:00.000', 4, 2, RIGHT(BeginTime, 2)), 1, 2, LEFT(BeginTime, 2)) AS DateTimeStr
FROM cte_BeginTimeFix
)
SELECT DateWithTime,
BeginTime,
CASE
WHEN ISDATE(DateTimeStr) = 1 THEN CAST(DateTimeStr AS DATETIME)
ELSE NULL
END AS DateTimeStr
FROM cte_DateString
A different approach is to convert the time column in minutes and add it to the date
DATEADD(minute, T0.BeginTime / 100 * 60 + T0.BeginTime % 100
, CONVERT(VARCHAR, T0.BeginDate, 112))
with that the length of the time column doesn't matter
This should work:
CONVERT
(
DATETIME,
CONVERT(VARCHAR,T0.Date,112) +
' ' +
CASE
WHEN ISNULL(T0.BeginTime,'0') = '0'
THEN '00:00'
ELSE
RIGHT
(
'00' + LEFT(T0.BeginTime,LEN(T0.BeginTime) - 3),
2
) +
':' +
RIGHT(T0.BeginTime,2)
END
)

SQL SELECT HH:MM - HH:MM

I am trying to write an SQL query that will return the current time as follows:
HH:MM - HH:MM (+1)
E.g. if the time is 14:00 it would return 14:00 - 15:00
I have managed to get as far as follows:
SELECT TOP (30)
DATEADD(HOUR, DATEDIFF(HOUR, 0, QUEUE_TIME.QueueDate), 0) AS 'DateAdded',
CAST(CONVERT(VARCHAR(2), DATEPART(HH, QUEUE_TIME.QueueDate), 108) AS VARCHAR(2)) + ' - ' +
CAST(CONVERT(VARCHAR(2), DATEPART(HH, QUEUE_TIME.QueueDate), 108) + 1 AS VARCHAR(2)) AS Interval,
QUEUE_TYPE.Name, QUEUE_TIME.QueueTypeId,
MAX(QUEUE_TIME.QueuedTimeInSec / 60) AS 'WaitingTimeInSec',
CAST(ROUND(AVG(QUEUE_TIME.FlowRateWhenJoinedPerMin), 1) AS numeric(36, 2)) AS AvgFlowRate
FROM
QUEUE_TIME
INNER JOIN
QUEUE_TYPE ON QUEUE_TIME.QueueTypeId = QUEUE_TYPE.Id
WHERE
(QUEUE_TIME.QueueDate >= '11/07/2014 00:00')
AND (QUEUE_TIME.IsFreeFlowing = '0')
AND (QUEUE_TIME.QueueTypeId = '3')
GROUP BY
DATEADD(HOUR, DATEDIFF(HOUR, 0, QUEUE_TIME.QueueDate), 0),
DATEPART(HOUR, QUEUE_TIME.QueueDate), QUEUE_TYPE.Name, QUEUE_TIME.QueueTypeId
ORDER BY
'DateAdded'
This will return as follows, e.g. if it is 8am it will return 8-9. But i need it to be in the format 08:00 - 09:00.
Any assistance would be appreciated.
I am using SQL Server 2008.
Thanks
This is an example, you can replace #now variable with column name:
declare #now datetime = getdate()
select convert(varchar(5), #now, 114) + ' - ' +
convert(varchar(5), dateadd(hour, 1, #now), 114) yourColumn
SQL Fiddle demo
Try with FORMAT for SQL Server 2012+:
SELECT FORMAT(GETDATE(), 'HH:mm') + '-' + FORMAT(DATEADD(HH, 1, GETDATE()), 'HH:mm')

DATEDIFF in HH:MM:SS format

I need to calculate the total length in terms of Hours, Minutes, Seconds, and the average length, given some data with start time and end time.
For example the result must be something like 45:15:10 which means 45 hours 15 min 10 sec, or 30:07 for 30 min 07 sec.
We're using SQL Server 2008 R2 and the conversion failed when time is more than 24:59:59. Any idea of how I could do this?
For information, the columns in the table are Id, StartDateTime, EndDateTime, etc. I need to make a monthly report which contains the recordings count of the month, the total length of these records, and the average length. I'd like to know if there is an easy way to perform all of this.
You shouldn't be converting to time - it is meant to store a point in time on a single 24h clock, not a duration or interval (even one that is constrained on its own to < 24 hours, which clearly your data is not). Instead you can take the datediff in the smallest interval required (in your case, seconds), and then perform some math and string manipulation to present it in the output format you need (it might also be preferable to return the seconds to the application or report tool and have it do this work).
DECLARE #d TABLE
(
id INT IDENTITY(1,1),
StartDateTime DATETIME,
EndDateTime DATETIME
);
INSERT #d(StartDateTime, EndDateTime) VALUES
(DATEADD(DAY, -2, GETDATE()), DATEADD(MINUTE, 15, GETDATE())),
(GETDATE() , DATEADD(MINUTE, 22, GETDATE())),
(DATEADD(DAY, -1, GETDATE()), DATEADD(MINUTE, 5, GETDATE())),
(DATEADD(DAY, -4, GETDATE()), DATEADD(SECOND, 14, GETDATE()));
;WITH x AS (SELECT id, StartDateTime, EndDateTime,
d = DATEDIFF(SECOND, StartDateTime, EndDateTime),
a = AVG(DATEDIFF(SECOND, StartDateTime, EndDateTime)) OVER()
FROM #d
)
SELECT id, StartDateTime, EndDateTime,
[delta_HH:MM:SS] = CONVERT(VARCHAR(5), d/60/60)
+ ':' + RIGHT('0' + CONVERT(VARCHAR(2), d/60%60), 2)
+ ':' + RIGHT('0' + CONVERT(VARCHAR(2), d % 60), 2),
[avg_HH:MM:SS] = CONVERT(VARCHAR(5), a/60/60)
+ ':' + RIGHT('0' + CONVERT(VARCHAR(2), a/60%60), 2)
+ ':' + RIGHT('0' + CONVERT(VARCHAR(2), a % 60), 2)
FROM x;
Results:
id StartDateTime EndDateTime delta_HH:MM:SS avg_HH:MM:SS
-- ------------------- ------------------- -------------- ------------
1 2013-01-19 14:24:46 2013-01-21 14:39:46 48:15:00 42:10:33
2 2013-01-21 14:24:46 2013-01-21 14:46:46 0:22:00 42:10:33
3 2013-01-20 14:24:46 2013-01-21 14:29:46 24:05:00 42:10:33
4 2013-01-17 14:24:46 2013-01-21 14:25:00 96:00:14 42:10:33
This isn't precisely what you asked for, as it won't show just MM:SS for deltas < 1 hour. You can adjust that with a simple CASE expression:
;WITH x AS (SELECT id, StartDateTime, EndDateTime,
d = DATEDIFF(SECOND, StartDateTime, EndDateTime),
a = AVG(DATEDIFF(SECOND, StartDateTime, EndDateTime)) OVER()
FROM #d
)
SELECT id, StartDateTime, EndDateTime,
[delta_HH:MM:SS] = CASE WHEN d >= 3600 THEN
CONVERT(VARCHAR(5), d/60/60) + ':' ELSE '' END
+ RIGHT('0' + CONVERT(VARCHAR(2), d/60%60), 2)
+ ':' + RIGHT('0' + CONVERT(VARCHAR(2), d % 60), 2),
[avg_HH:MM:SS] = CASE WHEN a >= 3600 THEN
CONVERT(VARCHAR(5), a/60/60) + ':' ELSE '' END
+ RIGHT('0' + CONVERT(VARCHAR(2), a/60%60), 2)
+ ':' + RIGHT('0' + CONVERT(VARCHAR(2), a % 60), 2)
FROM x;
This query changes the delta column in the 2nd row in the above result from 0:22:00 to 22:00.
I slightly modified Avinash's answer as it may end with error if difference is too big. If you need only HH:mm:ss it is sufficient to distinguish at seconds level ony like this:
SELECT CONVERT(time,
DATEADD(s,
DATEDIFF(s,
'2018-01-07 09:53:00',
'2018-01-07 11:53:01'),
CAST('1900-01-01 00:00:00.0000000' as datetime2)
)
)
SELECT CONVERT(time,
DATEADD(mcs,
DATEDIFF(mcs,
'2007-05-07 09:53:00.0273335',
'2007-05-07 09:53:01.0376635'),
CAST('1900-01-01 00:00:00.0000000' as datetime2)
)
)
If you want to do averages, then the best approach is to convert to seconds or fractions of a day. Day fractions are convenient in SQL Server, because you can do things like:
select avg(cast(endtime - starttime) as float)
from t
You can convert it back to a datetime using the reverse cast:
select cast(avg(cast(endtime - starttime as float) as datetime)
from t
The arithmetic to get the times in the format you want . . . that is a pain. You might consider including days in the final format, and using:
select right(convert(varchar(255), <val>, 120), 10)
To get the hours exceeding 24, here is another approach:
select cast(floor(cast(<val> as float)*24) as varchar(255))+right(convert(varchar(255), <val>, 120), 6)
It uses convert for minutes and seconds, which should be padded with 0s on the left. It then appends the hours as a separate value.
Starting in SQL SERVER 2012, you don't need to use DATEDIFF function. You can use FORMAT function to achieve what you want:
SELECT
FORMAT(CONVERT(TIME, [appoitment].[Start] - [appointment].[End]), N'hh\:mm') AS 'Duration'
FROM
[tblAppointment] (NOLOCK)
A way that avoids overflows and can include days and go all the way to milliseconds in the output:
DECLARE #startDate AS DATETIME = '2018-06-01 14:20:02.100'
DECLARE #endDate AS DATETIME = '2018-06-02 15:23:09.000'
SELECT CAST(DATEDIFF(day,'1900-01-01', #endDate - #startDate) AS VARCHAR) + 'd ' + CONVERT(varchar(22), #endDate - #startDate, 114)
The above will return
1d 01:03:06:900
And, off course, you can use the formatting of your choice
SQL Supports datetime substraction which outputs a new datetime relative to the MIN date (for instance 1900-01-01, you can probably get this value from some system variable) This works better than DATEDIFF, because DATEDIFF will count ONE for each "datepart boundaries crossed", even if the elapsed time is less than a whole datapart. Another nice thing about this method is that it allows you to use the date formatting conversions.
If days is the (positive) number of days, like 0.5 for 12 hours, use this expression to format it as a proper duration:
CONVERT(varchar(9), FLOOR(days * 24)) + RIGHT(CONVERT(char(19), CAST(days AS datetime), 120), 6)
Excel will understands values up to 9999:59:59 when pasted. There apply a custom format: [h]:mm:ss in the English version ([u]:mm:ss for Dutch).

Getting Time Interval from Datetime

I have a table called Timezone and the data looks like:
Call_ID Start_Time
93856 2011-08-04 09:59:47.000
58796 2011-08-05 14:54:37.000
25489 2011-08-09 15:32:13.000
I want the output as :
Call_ID Start_Time Interval
93856 2011-08-04 09:59:47.000 0930
58796 2011-08-05 14:54:37.000 1430
25489 2011-08-09 15:32:13.000 1530
I did something like this:
Select Call_ID , Start_Time,
CASE WHEN DATEPART(minute,Start_Time)>30 THEN
RIGHT('0' + CAST(DATEPART(HOUR,Start_Time) AS VARCHAR),2) + '30'
ELSE
RIGHT('0' + CAST(DATEPART(HOUR,Start_Time) AS VARCHAR),2) + '00'
END
From Timezone
Group By Call_ID , Start_Time,
CASE WHEN DATEPART(minute,Start_Time)>30 THEN
RIGHT('0' + CAST(DATEPART(HOUR,Start_Time) AS VARCHAR),2) + '30'
ELSE
RIGHT('0' + CAST(DATEPART(HOUR,Start_Time) AS VARCHAR),2) + '00'
END
Is there a better way of doing it?
select Call_ID,
Start_Time,
right(100+datepart(hour, Start_Time), 2)+
right(100+30*(datepart(minute, Start_Time)/30), 2) as Interval
from TimeZone
Not really any shorter, but certainly tidier with far less casts and string concatenation:
;WITH intervals(h) AS
(
SELECT TOP (48) CONVERT(TIME(0), DATEADD(MINUTE, 30*(number), '00:00'))
FROM master..spt_values
WHERE number >= 0
GROUP BY number
ORDER BY number
)
SELECT
t.Call_ID,
t.Start_Time,
Interval = REPLACE(CONVERT(VARCHAR(5), i.h), ':', '')
FROM intervals AS i
INNER JOIN dbo.TimeZone AS t
ON DATEDIFF(MINUTE, i.h, CONVERT(TIME(0), t.Start_Time)) BETWEEN 1 AND 30;
Not sure what you wanted to do if you have a value right on the boundary. Do you want it to fall in the current interval or the previous? You can change BETWEEN 1 AND 30 to BETWEEN 0 AND 29 if you want different behavior.