Combine date and time is very slow, how to make it efficient? - sql-server

This is my code
concat(CONVERT(varchar(10), cast(cast(cast(DATE1 as int) as char(8)) as date), 101),
Above part is creating date format and then concatenating below with:
', ',
convert(varchar(5), (convert(time, left(RIGHT('000000' + CONVERT(varchar(6), TIME1), 6), 2)
+ ':' + substring(RIGHT('000000' + CONVERT(varchar(6), TIME1), 6), 3, 2)
+ ':' + substring(RIGHT('000000' + CONVERT(varchar(6), TIME1), 6), 5, 2))), 108))
This returns the time.
Combining the two in same line outputs as mm/dd/yyy, 13:00
I am converting original fields where date is written as yyyymmdd and time is written as 5-6 digits, 71200 (7:12, but seconds always 00).
This takes significantly long, so I would like a way to make the code more efficient.
A note, I am concatenating date with 4 different time columns, so maybe that's why it is taking long?

Try using DATEADD instead of string manipulations:
DATEADD(MINUTE, TIME1%10000/100,
DATEADD(HOUR,TIME1/10000,
CAST(CAST(CAST(DATE1 as int) AS char(8)) AS datetime))
TIME1 / 10000 should give you the number of hours. 71200 -> 7
TIME1 % 10000 should give you the number of minutes * 100. 71200 -> 1200
TIME1 % 10000 / 100 will give you the number of minutes. 71200 -> 12
If you are querying this data often (more often than you are inserting/updating it), or you care more about query performance than updating/insert performance, then you should consider making a persisted computed column on this table so that you don't have to do these calculations at query time. You can then also index the column which will help immensely if you regularly filter based on the datetime.

Related

Summing Times as Durations

I am trying to sum a time field stored in my database as an nvarchar like 'hh:mm', in minutes, however, the following does not work (likely because time cannot handle anything over '23:59:59'):
SELECT
SUM(DATEDIFF(MINUTE, '00:00:00', MyTimeField)) AS TotalMinutes
FROM MyTable
Here's my SQL fiddle on this. Any help appreciated
24:00 is not valid, and if you try 00:00, you have already entered the next day - but because you are not considering a full date when using datediff, that cannot happen. Because if you mention 00:00, then that points at the beginning of the current day rather than the next.
Consider storing time as datetime rather than varchar - and store the clean up times as full time stamp rather than just stating the hour.
Here's the solution:
CREATE TABLE MyTable
([MyTimeField] nvarchar(5))
;
INSERT INTO MyTable
([MyTimeField])
VALUES
('00:15'),
('00:30'),
('01:45'),
('23:15'),
('23:59'),
('24:00')
;
WITH IntMinutes AS
(
SELECT
CASE
WHEN RIGHT('0000' + MyTimeField, 5) < '24:00' THEN DATEDIFF(MINUTE, '00:00:00', MyTimeField)
ELSE
DATEDIFF(MINUTE, '00:00:00', '00:' + RIGHT(MyTimeField, 2)) +
DATEDIFF(MINUTE, '00:00:00', CAST((LEFT(MyTimeField, 2) - 24) AS VARCHAR(2)) + ':00') +
DATEDIFF(MINUTE, '00:00:00', '23:59') + 1
END AS Minutes
FROM MyTable
)
SELECT SUM(Minutes) AS TotalMinutes
FROM IntMinutes

Convert number of hours to days and hours in SQL Server (NOT T-SQL)

I have a number of hours which I need to display in the format of days and hours.
This number is derived from a DATEDIFF instruction.
For numbers less than 24, I wish to display only hours - ie, 21 hours.
For larger numbers, I wish to display days and hours - ie, 3 days, 14 hours
I do not need to display any smaller unit than hours, and values should be rounded down to the preceding hour, so 1 hour and 59 minutes will be 1 hour.
I cannot use a stored procedure - this must run as a single select statement.
I am aware that I can calculate the value by using modulo, so assuming 71 hours:
select concat((71 - (71 % 24)) / 24, ' days, ', 71 % 24, ' hours')
This however is somewhat messy, and as the statement must be a single select, I will have to calculate the DATEDIFF 3 times as below.
SELECT CONCAT (
(DATEDIFF(HOUR, StartDate, EndDate) -
(DATEDIFF(HOUR, StartDate, EndDate) % 24)) / 24,
' days, ',
DATEDIFF(HOUR, StartDate, EndDate) % 24,
' hours')
FROM RecordsTable
Is it possible to either format a number of hours as days and hours directly using an inbuilt SQL command, or failing that, select (datediff(hour, StartDate, EndDate) into a variable which I can reuse in the single select?
EDIT - As suggested, the solution was to use a CTE as follows:
WITH totalhours (htotal) AS
(
SELECT
DATEDIFF(HOUR, StartDate, EndDate) AS htotal
FROM
RecordsTable
)
SELECT
CONCAT ((htotal - (htotal % 24)) / 24,
' days, ',
htotal % 24,
' hours')
FROM
RecordsTable;
Use a CTE to generate your total once, and reference that total in your select against the CTE. Or use a subquery to generate the total once and then select from the subquery to get the desired results.
The fundamental issue is you need to materialize the total once to be able to reference it; forcing the engine to materialize a value is generally done via a CTE or subquery.
You can do a lot with datetime objects and format strings or datepart. For example,
declare #n int = 105;
select format(dateadd(day, -1, dateadd(hour, #n, '1753-1-1')), 'd h');
-- 4 9
Taking the minimum datetime value (1753-01-01), adding the requisite number of hours, subtracting one day (because on the first day you want days = 0), and then formatting.
You could improve the formatting like this:
select format(dateadd(day, -1, dateadd(hour, #n, '1753-1-1')), 'd \da\y(\s), h \hour(\s)');
-- 4 day(s), 9 hour(s)
Of course this will only work up to 31 days, because then you'll be out of the month of January in 1753 and into February. If that's the case, revert to datepart. This is uglier, but will work for larger values
select
datepart(day, (dateadd(day, -1, dateadd(hour, #n, '1753-1-1')))),
datepart(hour, (dateadd(day, -1, dateadd(hour, #n, '1753-1-1'))));

DATEDIFF - Display result in Hours and minutes

I am using the below to determine the minute between two times,
DateDiff(Minute, [MondayOpenTime] , [MondayCloseTime])
For a start time of 09:00 and a Close time of 17:30 it returns the value 510
Or if i use:
DateDiff(hour, [MondayOpenTime] , [MondayCloseTime])
It returns 8,
How do i get it to return the hours and minutes like 08:30
Using the minute, and then divide by 60, gives 8.5,
Note. I have been through many of the similar questions to this, if there is one with an exact answer i will delete this, but i cannot find any answers as of yet
You can calculate the hours by division as you stated. You can then use modulo to calculate the minutes.
declare #MondayOpenTime time = '09:00'
, #MondayCloseTime time = '17:30'
select convert(varchar(2), DateDiff(Minute, #MondayOpenTime , #MondayCloseTime) / 60) + ':' + right('0' + convert(varchar(2), DateDiff(Minute, #MondayOpenTime , #MondayCloseTime) % 60), 2)
For datetime(2) datatypes, the following could be used:
format([MondayCloseTime] - [MondayOpenTime], 'HH:mm')
Provided that the dates fall on the same day. (since these are opening times, that shouldn't be a problem)
As Sean pointed out: for time datatypes, subtraction isn't directly possible and an extra cast is needed:
format(cast([MondayCloseTime] as datetime) - cast([MondayOpenTime] as datetime), 'HH:mm')
(sidenote: format is only available on sql-server 2012 and higher)

how to use sql to get number of seconds from a string formatted like "HH:MM:SS"

I have a C# program that recorded a TimeSpan value into a SQL Server table's varchar field. For example, the varchar field might have the value "00:12:05.7989790".
How could I use SQL code to get that total value in SECONDS? Since that varchar represents 12 minutes and 5 seconds, I would like a SQL statement that extracts it as "725".
I've tried some code like this:
select
Case when IsDate(the_value)=1 then datepart(HOUR, CONVERT(datetime, the_value))*360 else 0 end
+ Case when IsDate(the_value)=1 then datepart(MINUTE, CONVERT(datetime, the_value))*60 else 0 end
+ Case when IsDate(the_value)=1 then datepart(SECOND, CONVERT(datetime, the_value))*1 else 0 end
from mytable
but it complains "Conversion failed when converting date and/or time from character string."
Any suggestions would be greatly appreciated.
DATEDIFF function
https://msdn.microsoft.com/en-AU/library/ms189794.aspx
DATEDIFF(ss, '00:00:00.000', [your time column])
I would use datediff() and use explicit conversion to time:
select datediff(second, '00:00:00', convert(time, the_value))
If the units can exceed 23 hours, then you have a challenge, because the conversion will fail. In this case, string manipulation is an option:
select (left(the_value, 2) * 60 * 60 +
substring(the_value, 3, 2) * 60 +
substring(the_value, 5, 2)
) as seconds
This assumes that all strings are in the proper format. You can validate this with a case:
select (case when the_value like '[0-9]0-9]:[0-9[0-9]:[0-9][0-9]%'
then (left(the_value, 2) * 60 * 60 +
substring(the_value, 3, 2) * 60 +
substring(the_value, 5, 2)
)
end) as seconds

Cannot get hours to total over 24, eg 48 hours in hh:mm:ss output

I am running a query to find out the total amount of time a user has been browsing for. Each browsing session is stored in the DB as seconds and I then sum the total seconds and convert it into hh:mm:ss. The problem is when I'm converting the seconds into hh:mm:ss. I want it to display for example '78:20:00' but I dont know how to get the code to total it like this. When it gets past 24 hrs the hrs column goes back to 00 because its into a day.
The query I run to convert the time can be seen below:
SELECT Username,
CONVERT(VARCHAR(12),DATEADD(SECOND,TotalTimeInSeconds,0),108) AS TotalHours
FROM #TotalSessionTime
SELECT USERNAME,
CAST(TotalTimeInSeconds / (60 * 60) AS Varchar) + ':' +
CAST(TotalTimeInSeconds % (60 * 60) / 60 AS Varchar) + ':' +
CAST(TotalTimeInSeconds % (60) AS Varchar) AS TotalHours
FROM #TotalSessionTime
If you want minutes and seconds to always be two digits, you'll have to left pad them, which would make for an ugly example, but would work fine.
Try this...
DECLARE #TimeInSeconds int = 123400;
DECLARE #MyDate datetime;
SELECT #MyDate = CONVERT( DateTime, DATEADD( SECOND, #TimeInSeconds, 0))
SELECT CONVERT(CHAR(2), (DAY( #MyDate )-1) * 24 + DATEPART(hour,#MyDate)) + ':' + CONVERT(CHAR(2), DATEPART(minute, #MyDate)) + ':' + CONVERT(CHAR(2), DATEPART(SECOND, #MyDate))
You'll want to left-pad the minutes and seconds with a zero to be sure it's 2-digits, though.

Resources