How to add two time values together in SQL - sql

I have a table with 'accumulate_low_risk' and 'accumulate_high_risk'. Both are timedata (time(7)) in the format 00:00:00.0000000.
I want to add them together and place them into another timedata called 'combined_shift_total'
I have tried to convert them to seconds, add the results together and convert them back to timedata but i'm getting lost.
The table is as follows:
CREATE TABLE accumulate_time ('id INT IDENTITY(1,1) PRIMARY KEY,time DATETIME NOT NULL,worker_id INTEGER NOT NULL REFERENCES worker_table(worker_id),shift_start DATETIME NOT NULL,accumulate_low_risk TIME NOT NULL,accumulate_high_risk TIME NOT NULL,combined_shift_total TIME NOT NULL)
I was trying something along the lines of....
select Sum(Left (CONVERT(nvarchar(30),accumulate_low_risk, 126),2) * 3600 + substring(accumulate_low_risk, 4,2) * 60 + substring(accumulate_low_risk, 7,2))
from accumulate_time WHERE id IN (SELECT max(id) FROM accumulate_time WHERE worker_id = 6)
But this is way too messy.
My knowledge of SQL is limited so I'm sure there's a simple way to achieve this.
I would be very grateful for any and all help.
Thanks!

To me it looks like SQL Server. You can achieve it by counting how many seconds you have in your columns using datediff() and then add it to 00:00:00.0000000 using dateadd(). Please note that I don't know how precise is your data, so if you want it to me more precise you need to i.e. change second to milisecond. It's shift data so it shouldn't excess 24 hours, but please note what in this case it wouldn't work.
select cast(dateadd(second, datediff(second, '00:00:00.0000000', accumulate_low_risk) + datediff(second, '00:00:00.0000000', accumulate_high_risk), '2019-01-01') as time(7))
from accumulate_time
An examply how it works on a temp table:
create table #time (t1 time(7), t2 time(7))
insert into #time values ('01:12:04.0000000','00:12:04.0000000')
select cast(dateadd(second, datediff(second, '00:00:00.0000000', t1) + datediff(second, '00:00:00.0000000', t2), '2019-01-01') as time(7))
from #time

Related

SQL Creating a table for song duration

I am trying to create a table in SQL which is about Music, it should contain songDuration. Which means I gotta hold minutes:seconds information in the table. But i have no idea what to use for the type. I am using SQL server.
Edit: I want to use the database for an ASP.NET Core web application. I was using a ready-to-use SQL database like northwnd. Now, I am trying to create one. So, I will not see the timing with SELECT function in SQL query. So, I need to use something that makes it mm:ss otomaticly. Is there is a type that I can decleare like that?
create table musics(
songDuration type,
...)
Why just don't you use int?
So you could calculate duration in the way you like.
E.g. minutes,hours, etc.
There's a datatype time which would be the logical choice, that stores it in format HH:mm:ss with an optional amount of fractional seconds determined by the size you declare the field (e.g. time(3) holds it to three decimal places)
If your source data is already in this notation it makes it getting it in the table easy and simple sorting/filtering operates as you expect. The downside to doing this is if you want to do certain operations such as SUM or AVG (because as a_horse_with_no_name pointed out in their comment) time technically represents a point in time not a duration and you'd have to do some workaround like so:
SELECT totalduration = SUM(DATEDIFF(SECOND, '0:00:00', duration))
FROM dbo.table
Alternatively you could store the number of (say) seconds in the duration using an int, but if you don't already have the information in that format you'd have to do some (light) conversion when inserting the data, and then back if you want to display in mm:ss
e.g:
SELECT CONVERT(varchar, DATEADD(SECOND, durationinseconds, 0), 8) FROM dbo.table
which would convert it back to hh:mm:ss format.
I do this by using an int column, and store the seconds there.
In your client you can calculate from the seconds howmany days, hours, minutes and seconds it is and display it like you want.
To display it in sql you can use this for example
declare #seconds int = 350
select right('00' + convert(varchar, datepart(hour, dateadd(s, #seconds, 0)) + ((#seconds / 86400) * 24)), 2)
+ ':' + right('00' + convert(varchar, datepart(minute, dateadd(s, #seconds, 0))), 2)
+ ':' + right('00' + convert(varchar, datepart(second, dateadd(s, #seconds, 0))), 2)
this will display
00:05:50
which is 0 hours, 5 minutes and 50 seconds
IF the value of seconds is larger than a day, this will be no problem. The number of hours will simply be greater
a value of 350000 will display 97:13:20

Convert varchar of HH:MM:SS to minutes value

Using SQL Server, I have a column with varchar data coming from another source that is formatted in pseudo HH:MM:SS string representing a duration time like:
Duration
HH:MM:SS
00:43:46
01:30:06
43:56:38
89:24:00
5890:01:00
I wanted to convert it to a simple minutes (int) value for each
Duration MinuteDuration
HH:MM:SS mm
00:43:46 43
01:30:06 90
43:56:38 2636
89:24:00 5364
5890:01:00 353401
I looked around stackoverflow and found several people suggesting CONVERT with the TIME param
USE [MyDB]
GO
SELECT [User],
[Duration],
(SELECT CONVERT(TIME, Duration, 8)) as DurationMinutes,
FROM [dbo].[MyTable]
GO
but I cannot use that since my hours values may be larger than 24/12 (my hours value could be in the thousands). Since the original data is in a varchar, I need to interpret the string first, then multiple the hours by 60* and add it to the minutes value (and just drop the seconds value).
Although parsing the components of the time is definitely a possibility, the string manipulation is pretty simple too:
select duration,
(convert(int, left(duration, charindex(':', duration) - 1)) * 60 +
convert(int, left(right(duration, 5), 2))
)
from t;
Here is a db<>fiddle.
Another option is ParseName() in concert with a CROSS APPLY
Example
Declare #YourTable Table ([Duration] varchar(50))
Insert Into #YourTable Values
('00:43:46')
,('01:30:06')
,('43:56:38')
,('89:24:00')
,('5890:01:00')
Select A.Duration
,Minutes = parsename(NewValue,2) + (parsename(NewValue,3)*60)
from #YourTable A
Cross Apply ( values (replace(Duration,':','.') ) ) B(NewValue)
Returns
Duration Minutes
00:43:46 43
01:30:06 90
43:56:38 2636
89:24:00 5364
5890:01:00 353401

SQL convert single int to a time value

I am trying to convert a single integer which represents the hour value into a time. I've tried using cast but this converts the value to a date
cast(datepart(hh,tstart) as datetime) as test
I've also tried casting it as a time and the conversion is not allowed.
The numbers I am working with are 7,8,9,10,11,12,13,...,23, 0
The format I would like is convert 7 to 7:00, 23 to 23:00, etc
Thank you
There are many ways to do that:
WITH table_name AS
(
SELECT * FROM (VALUES
(1),(2),(3),(10),(23)
) T(H)
)
SELECT DATEADD(HOUR, H, 0) DateValue,
CONVERT(time, DATEADD(HOUR, H, 0)) TimeValue,
CONVERT(varchar(2), H)+':00' TextValue
FROM table_name T1
I would recomend storing value as TIME datatype.
You can try this:
select cast(dateadd(hour,3,'00:00:00') as time)
which gives result:
03:00:00.0000000
Use your int values in place of 3 in above statement
Use convert datetime to varchar datatype and use case statement
DECLARE #date datetime ='2016-05-01 10:00:000'
SELECT CASE cast(DATEPART(hh,#date)as varchar(10))
WHEN '10'then '10:00'
WHEN '11'then '11:00'
WHEN '12'then '12:00'
.
.
.
WHEN '23' then '23:00'
END 'Hourpart'
You can concatenate a minute to the time and cast it to time
select cast(concat(20,':00') as time(7)) as 'time'
If you wanted to display like 'HH:MM',use the below query.
SELECT CAST(23 AS VARCHAR(50))+':00'
If you wanted to get the result as time format,use the below query.
SELECT CAST(CAST(23 AS VARCHAR(50))+':00:00' AS TIME)
OR
SELECT CAST(DATEADD(HOUR,23,'00:00:00') as time)
here is the sample output :
Assuming all of your potential values are integers between 1 and 24, I think FORMAT is the simplest way.
FORMAT(tstart*100,'00:00')

How to Handle DATEDIFF(MINUTE, '00:00', '24:20') Like scenario?

There is a column in my Table. In which we are storing string value in format 'HH:MM'.During fetching records with this table every things works ok with
DATEDIFF(MINUTE, '00:00', ColumnName)
Problem is when we have Value greater than 23:59.
Its showing error like
Conversion failed when converting date and/or time from character string.
Can anybody suggest me the right approach for achieving this scenario.
If you are storing the value as something other than a time, why not just store the number of minutes and convert to whatever format you want on output?
Otherwise, I would suggest that you simply convert the value to minutes:
select (cast(left(ColumnName, 2) as int) * 60 +
cast(right(ColumnName, 2) as int)
) as Minutes
If you are not using date/time values, there is no requirement for using the functions specifically designed for them.
EDIT:
To handle hours longer than 99, use charindex():
select (cast(left(ColumnName, charindex(':', ColumnName) - 1) as int) * 60 +
cast(right(ColumnName, 2) as int)
) as Minutes
So it sounds like your saving the length of a time period. Try storing it in minutes. My query can handle numbers of different lengths since it's based on the colon.
DECLARE #yourTable TABLE (ColumnName VARCHAR(10));
INSERT INTO #yourTable
VALUES ('100:00'),
('24:20');
SELECT ColumnName,
(hr * 60) + minut AS time_period_in_minutes
FROM #yourTable
CROSS APPLY (SELECT CAST(SUBSTRING(ColumnName,0,CHARINDEX(':',ColumnName)) AS INT),
CAST(SUBSTRING(ColumnName,CHARINDEX(':',ColumnName) + 1,LEN(ColumnName)) AS INT)) CA(hr,minut)
Results:
ColumnName time_period_in_minutes
---------- ----------------------
100:00 6000
24:20 1460
Try to this
select DATEDIFF(MINUTE, '00:00', case when ISDATE(ColumnName)=0 then '00:00' else ColumnName end )

How can I compare time in SQL Server?

I'm trying to compare time in a datetime field in a SQL query, but I don't know if it's right. I don't want to compare the date part, just the time part.
I'm doing this:
SELECT timeEvent
FROM tbEvents
WHERE convert(datetime, startHour, 8) >= convert(datetime, #startHour, 8)
Is it correct?
I'm asking this because I need to know if 08:00:00 is less or greater than 07:30:00 and I don't want to compare the date, just the time part.
Thanks!
Your compare will work, but it will be slow because the dates are converted to a string for each row. To efficiently compare two time parts, try:
declare #first datetime
set #first = '2009-04-30 19:47:16.123'
declare #second datetime
set #second = '2009-04-10 19:47:16.123'
select (cast(#first as float) - floor(cast(#first as float))) -
(cast(#second as float) - floor(cast(#second as float)))
as Difference
Long explanation: a date in SQL server is stored as a floating point number. The digits before the decimal point represent the date. The digits after the decimal point represent the time.
So here's an example date:
declare #mydate datetime
set #mydate = '2009-04-30 19:47:16.123'
Let's convert it to a float:
declare #myfloat float
set #myfloat = cast(#mydate as float)
select #myfloat
-- Shows 39931,8244921682
Now take the part after the comma character, i.e. the time:
set #myfloat = #myfloat - floor(#myfloat)
select #myfloat
-- Shows 0,824492168212601
Convert it back to a datetime:
declare #mytime datetime
set #mytime = convert(datetime,#myfloat)
select #mytime
-- Shows 1900-01-01 19:47:16.123
The 1900-01-01 is just the "zero" date; you can display the time part with convert, specifying for example format 108, which is just the time:
select convert(varchar(32),#mytime,108)
-- Shows 19:47:16
Conversions between datetime and float are pretty fast, because they're basically stored in the same way.
convert(varchar(5), thedate, 108) between #leftTime and #rightTime
Explanation:
if you have varchar(5) you will obtain HH:mm
if you have varchar(8) you obtain HH:mm ss
108 obtains only the time from the SQL date
#leftTime and #rightTime are two variables to compare
If you're using SQL Server 2008, you can do this:
WHERE CONVERT(time(0), startHour) >= CONVERT(time(0), #startTime)
Here's a full test:
DECLARE #tbEvents TABLE (
timeEvent int IDENTITY,
startHour datetime
)
INSERT INTO #tbEvents (startHour) SELECT DATEADD(hh, 0, GETDATE())
INSERT INTO #tbEvents (startHour) SELECT DATEADD(hh, 1, GETDATE())
INSERT INTO #tbEvents (startHour) SELECT DATEADD(hh, 2, GETDATE())
INSERT INTO #tbEvents (startHour) SELECT DATEADD(hh, 3, GETDATE())
INSERT INTO #tbEvents (startHour) SELECT DATEADD(hh, 4, GETDATE())
INSERT INTO #tbEvents (startHour) SELECT DATEADD(hh, 5, GETDATE())
--SELECT * FROM #tbEvents
DECLARE #startTime datetime
SET #startTime = DATEADD(mi, 65, GETDATE())
SELECT
timeEvent,
CONVERT(time(0), startHour) AS 'startHour',
CONVERT(time(0), #startTime) AS '#startTime'
FROM #tbEvents
WHERE CONVERT(time(0), startHour) >= CONVERT(time(0), #startTime)
Just change convert datetime to time that should do the trick:
SELECT timeEvent
FROM tbEvents
WHERE convert(time, startHour) >= convert(time, #startHour)
if (cast('2012-06-20 23:49:14.363' as time) between
cast('2012-06-20 23:49:14.363' as time) and
cast('2012-06-20 23:49:14.363' as time))
One (possibly small) issue I have noted with the solutions so far is that they all seem to require a function call to process the comparison. This means that the query engine will need to do a full table scan to seek the rows you are after - and be unable to use an index. If the table is not going to get particularly large, this probably won't have any adverse affects (and you can happily ignore this answer).
If, on the other hand, the table could get quite large, the performance of the query could suffer.
I know you stated that you do not wish to compare the date part - but is there an actual date being stored in the datetime column, or are you using it to store only the time? If the latter, you can use a simple comparison operator, and this will reduce both CPU usage, and allow the query engine to use statistics and indexes (if present) to optimise the query.
If, however, the datetime column is being used to store both the date and time of the event, this obviously won't work. In this case if you can modify the app and the table structure, separate the date and time into two separate datetime columns, or create a indexed view that selects all the (relevant) columns of the source table, and a further column that contains the time element you wish to search for (use any of the previous answers to compute this) - and alter the app to query the view instead.
Using float does not work.
DECLARE #t1 datetime, #t2 datetime
SELECT #t1 = '19000101 23:55:00', #t2 = '20001102 23:55:00'
SELECT CAST(#t1 as float) - floor(CAST(#t1 as float)), CAST(#t2 as float) - floor(CAST(#t2 as float))
You'll see that the values are not the same (SQL Server 2005). I wanted to use this method to check for times around midnight (the full method has more detail) in which I was comparing the current time for being between 23:55:00 and 00:05:00.
Adding to the other answers:
you can create a function for trimming the date from a datetime
CREATE FUNCTION dbo.f_trimdate (#dat datetime) RETURNS DATETIME AS BEGIN
RETURN CONVERT(DATETIME, CONVERT(FLOAT, #dat) - CONVERT(INT, #dat))
END
So this:
DECLARE #dat DATETIME
SELECT #dat = '20080201 02:25:46.000'
SELECT dbo.f_trimdate(#dat)
Will return
1900-01-01 02:25:46.000
Use Datepart function: DATEPART(datepart, date)
E.g#
SELECT DatePart(#YourVar, hh)*60) +
DatePart(#YourVar, mi)*60)
This will give you total time of day in minutes allowing you to compare more easily.
You can use DateDiff if your dates are going to be the same, otherwise you'll need to strip out the date as above
You can create a two variables of datetime, and set only hour of date that your need to compare.
declare #date1 datetime;
declare #date2 datetime;
select #date1 = CONVERT(varchar(20),CONVERT(datetime, '2011-02-11 08:00:00'), 114)
select #date2 = CONVERT(varchar(20),GETDATE(), 114)
The date will be "1900-01-01" you can compare it
if #date1 <= #date2
print '#date1 less then #date2'
else
print '#date1 more then #date2'
SELECT timeEvent
FROM tbEvents
WHERE CONVERT(VARCHAR,startHour,108) >= '01:01:01'
This tells SQL Server to convert the current date/time into a varchar using style 108, which is "hh:mm:ss". You can also replace '01:01:01' which another convert if necessary.
I believe you want to use DATEPART('hour', datetime).
Reference is here:
http://msdn.microsoft.com/en-us/library/ms174420.aspx
I don't love relying on storage internals (that datetime is a float with whole number = day and fractional = time), but I do the same thing as the answer Jhonny D. Cano. This is the way all of the db devs I know do it. Definitely do not convert to string. If you must avoid processing as float/int, then the best option is to pull out hour/minute/second/milliseconds with DatePart()
I am assuming your startHour column and #startHour variable are both DATETIME; In that case, you should be converting to a string:
SELECT timeEvent
FROM tbEvents
WHERE convert(VARCHAR(8), startHour, 8) >= convert(VARCHAR(8), #startHour, 8)
below query gives you time of the date
select DateAdd(day,-DateDiff(day,0,YourDateTime),YourDateTime) As NewTime from Table
#ronmurp raises a valid concern - the cast/floor approach returns different values for the same time. Along the lines of the answer by #littlechris and for a more general solution that solves for times that have a minute, seconds, milliseconds component, you could use this function to count the number of milliseconds from the start of the day.
Create Function [dbo].[MsFromStartOfDay] ( #DateTime datetime )
Returns int
As
Begin
Return (
( Datepart( ms , #DateTime ) ) +
( Datepart( ss , #DateTime ) * 1000 ) +
( Datepart( mi , #DateTime ) * 1000 * 60 ) +
( Datepart( hh , #DateTime ) * 1000 * 60 * 60 )
)
End
I've verified that it returns the same int for two different dates with the same time
declare #first datetime
set #first = '1900-01-01 23:59:39.090'
declare #second datetime
set #second = '2000-11-02 23:56:39.090'
Select dbo.MsFromStartOfDay( #first )
Select dbo.MsFromStartOfDay( #second )
This solution doesn't always return the int you would expect. For example, try the below in SQL 2005, it returns an int ending in '557' instead of '556'.
set #first = '1900-01-01 23:59:39.556'
set #second = '2000-11-02 23:56:39.556'
I think this has to do with the nature of DateTime stored as float. You can still compare the two number, though. And when I used this approach on a "real" dataset of DateTime captured in .NET using DateTime.Now() and stored in SQL, I found that the calculations were accurate.
TL;DR
Separate the time value from the date value if you want to use indexes in your search (you probably should, for performance). You can: (1) use function-based indexes or (2) create a new column for time only, index this column and use it in you SELECT clause.
Keep in mind you will lose any index performance boost if you use functions in a SQL's WHERE clause, the engine has to do a scan search. Just run your query with EXPLAIN SELECT... to confirm this. This happens because the engine has to process EVERY value in the field for EACH comparison, and the converted value is not indexed.
Most answers say to use float(), convert(), cast(), addtime(), etc.. Again, your database won't use indexes if you do this. For small tables that may be OK.
It is OK to use functions in WHERE params though (where field = func(value)), because you won't be changing EACH field's value in the table.
In case you want to keep use of indexes, you can create a function-based index for the time value. The proper way to do this (and support for it) may depend on your database engine. Another option is adding a column to store only the time value and index this column, but try the former approach first.
Edit 06-02
Do some performance tests before updating your database to have a new time column or whatever to make use of indexes. In my tests, I found out the performance boost was minimal (when I could see some improvement) and wouldn't be worth the trouble and overhead of adding a new index.