Addition of total hours in SQL Server - sql

In my database, I have a table with a column of type Float which represents the total work hours. I have to sum this value over all rows.
I can't use sum function, because 08.03 = 08 hour, 03 minute.
For example:
00.55 + 00.51=> 01.06
but I want
00.55 + 00.51 => 01.46
My query
Declare #TEMP1 table (TotalHours float)
insert into #TEMP1(TotalHours)
select ol.HrWork TotalHours
from OTLog ol
where EmployeeId = 2048
Declare #attCount int
Declare #attSum int
set #attSum = 0;
set #attCount = (select count(*) from #TEMP1)
print #attCount
while (#attCount <> 0)
begin
set #attSum = #attSum + (select Top 1 (LEFT(TotalHours,1)*60)+(RIGHT(TotalHours,2)) TotalHours from #TEMP1);
delete top(1) from #TEMP1;
set #attCount = #attCount - 1;
print #attSum;
end
print #attSum
select *
from OTLog
where EmployeeId = 2048
Any thoughts would be appreciated.

should use proper data type for storing time and not float
You need to break the float time into hour & minute. sum() it and convert back to your float format
select convert(int, total_minute / 60) + (total_minute % 60) / 100.0
from
(
select total_minute = sum(convert(int ,([hour] * 60) + [minute]))
from
(
select hour = floor(TotalHours), minute = convert(int, TotalHours * 100) % 100
from #TEMP1
) d
) d

You can try to use left(col1,2) to get hour number,right(col1,2) get minute number.
then use SUM to get totle hours and minutes in a subquery.
Then use DATEADD to add time value.
CREATE TABLE T(
col1 VARCHAR(10)
);
INSERT INTO T VALUES ('00.55');
INSERT INTO T VALUES ('00.51');
Query 1:
select FORMAT(DATEADD(minute,m,0)+ DATEADD(hour,h,0),'HH:mm:ss')
from (
SELECT SUM(CAST(left(col1,2) AS INT)) h ,SUM(CAST(right(col1,2) AS INT)) m
FROM T
) t1
Results:
| |
|----------|
| 01:46:00 |

Few issues with your problem:
first, float is not good choice for storing time information, look at date, time, datetime, timestamp, datetime2 - choose one of these, it will prevent such problems from occuring plus you have methods in SQL to work with such datatypes.
second - float is just approximation!, decimal stores exact values, you must be aware of that,
third - you need to be careful when dealing with decimal values in SQL Server, see Precision, scale, and Length (Transact-SQL) and this thread: Why is 199.96 - 0 = 200 in SQL?
After all, I can suggest this query to solve your problem:
select id,
sum(hours) + floor(sum(minutes) / 60) hours,
sum(minutes) % 60 minutes
from (
select id,
floor([time]) [hours],
cast([time] * 100 as int) % 100 [minutes]
from tblFloat
) a group by id
See this SQL fiddle, there you can test difference between using float and decimal (see the differences in a queries).

Related

How to calculate the sum of time with SQL SERVER? [duplicate]

I have a column called "WrkHrs" and the data type is time(hh:mm:ss). I want to sum up the working hours for employees. But since it's time data type sql server doesn't let me use like sum(columnname).
How can I sum up the time data type fieled in sql query?
SELECT EmployeeID, minutes_worked = SUM(DATEDIFF(MINUTE, '0:00:00', WrkHrs))
FROM dbo.table
-- WHERE ...
GROUP BY EmployeeID;
You can format it pretty on the front end. Or in T-SQL:
;WITH w(e, mw) AS
(
SELECT EmployeeID, SUM(DATEDIFF(MINUTE, '0:00:00', WrkHrs))
FROM dbo.table
-- WHERE ...
GROUP BY EmployeeID
)
SELECT EmployeeID = e,
WrkHrs = RTRIM(mw/60) + ':' + RIGHT('0' + RTRIM(mw%60),2)
FROM w;
However, you're using the wrong data type. TIME is used to indicate a point in time, not an interval or duration. Wouldn't it make sense to store their work hours in two distinct columns, StartTime and EndTime?
In order to sum up the working hours for an employee you can calculate the difference between the shift start time and end time in minutes and convert it to readable format as following:
DECLARE #StartTime datetime = '08:00'
DECLARE #EndTime datetime = '10:47'
DECLARE #durMinutes int
DECLARE #duration nvarchar(5)
SET #durMinutes = DATEDIFF(MINUTE, #StartTime, #EndTime)
SET #duration =
(SELECT RIGHT('00' + CAST((#durMinutes / 60) AS VARCHAR(2)),2) + ':' +
RIGHT('00' + CAST((#durMinutes % 60) AS VARCHAR(2)), 2))
SELECT #duration
The result : 02:47
two hours and 47 minutes
select DATEDIFF(MINUTE, '0:00:00', '00:02:08')
results in :- 2
select DATEDIFF(SECOND, '0:00:00', '00:02:08')
results in :- 128
Using seconds gives a better answer.
So I think the answer can be
SELECT
EmployeeId
, seconds_worked = SUM (DATEDIFF (SECOND, '0:00:00', WrkHrs))
FROM
tbl_employee
GROUP BY
EmployeeId;
DECLARE #Tab TABLE
(
data CHAR(5)
)
INSERT #Tab
SELECT '25:30' UNION ALL
SELECT '31:45' UNION ALL
SELECT '16:00'
SELECT STUFF(CONVERT(CHAR(8), DATEADD(SECOND, theHours + theMinutes,
'19000101'), 8), 1, 2, CAST((theHours + theMinutes) / 3600 AS VARCHAR(12)))
FROM (
SELECT ABS(SUM(CASE CHARINDEX(':', data) WHEN 0 THEN 0 ELSE 3600 *
LEFT(data, CHARINDEX(':', data) - 1) END)) AS theHours,
ABS(SUM(CASE CHARINDEX(':', data) WHEN 0 THEN 0 ELSE 60 *
SUBSTRING(data, CHARINDEX(':', data) + 1, 2) END)) AS theMinutes
FROM #Tab
) AS d
For MS SQL Server, when your WorkingTime is stored as a time, or a varchar in order to sum it up you should consider that:
1) Time format is not supporting sum, so you need to parse it
2) 23:59:59.9999999 is the maximum value for the time.
So, the code that will work to get you the total number of WorkingHours:WorkingMinutes:WorkingSeconds would be the following:
SELECT
CAST(FORMAT((SUM((DATEPART("ss",WorkingTime) + DATEPART("mi",WorkingTime) * 60 + DATEPART("hh",WorkingTime) * 3600)) / 3600),'00') as varchar(max)) + ':' +
CAST(FORMAT((SUM((DATEPART("ss",WorkingTime) + DATEPART("mi",WorkingTime) * 60 + DATEPART("hh",WorkingTime) * 3600)) % 3600 / 60),'00') as varchar(max)) + ':' +
CAST(FORMAT((SUM((DATEPART("ss",WorkingTime) + DATEPART("mi",WorkingTime) * 60 + DATEPART("hh",WorkingTime) * 3600)) % 3600 % 60),'00') as varchar(max)) as WorkingTimeSum
FROM TableName
It must be as simple as that.
Steps
convert time to seconds
sum the RESULT
convert the sum to time
Eg:
take a case you might want to sum the following time:
| present_hours |
|-----------------|
| 00:01:20.000000 |
|-----------------|
| 00:01:13.000000 |
|-----------------|
| 00:01:45.000000 |
|-----------------|
| 00:01:03.000000 |
|-----------------|
| 00:01:10.000000 |
|-----------------|
| 00:00:56.000000 |
SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(present_hours))) as total_present_hours FROM time_booking;

Convert non-standard mm:ss to hh:mm:ss with T-SQL

I am doing some data clean up from an old, text-based system. The system tracks running time of films with a format like this: '86:08'.
This means 1 hour 26 minutes and 8 seconds.
I am able to convert the first part like this:
declare #t varchar(10)
set #t = '86:08'
select #t as t,
t2 = try_convert(time,format(dateadd(minute,cast(left(#t, 2) as int),0),'hh:mm','en-US'))
Result:
t t2
86:08 01:26:00.0000000
How can I get the seconds as well, so the result would be 01:26:08:0000?
You could recover the 2 rightmost digits and convert to seconds instead of minutes.
Consider:
try_convert(
time,
format(
dateadd(second,cast(left(#t, 2) as int)*60 + cast(right(#t, 2) as int), 0),
'hh:mm:ss','en-US')
)
Demo on DB Fiddle:
declare #t varchar(10)
set #t = '86:08'
select
#t as t,
t2 = try_convert(
time,
format(
dateadd(second,cast(left(#t, 2) as int)*60 + cast(right(#t, 2) as int), 0),
'hh:mm:ss','en-US')
)
GO
t | t2
:---- | :-------
86:08 | 01:26:08
Here you go:
You need to multiply out to milliseconds as the fractional part is discarded.
SELECT DATEADD(ms, 86.08 * 1000, 0)
If you want it without the date portion you can use CONVERT, with style 114
SELECT CONVERT(varchar, DATEADD(ms, 86.08 * 1000, 0), 114)
You can convert the value to seconds and back to a time:
select #t as t,
convert(time, dateadd(second, left(#t, 2) * 60 + right(#t, 2), 0))

Filter time values that are a set period either side of a specified time

Given a specified time value and an interval value:
Specified Time: 13:25:00
Interval Value: 00:20:00
How can I filter the following table of values to return times that are the specified Interval either side of the Specified Time.
12:45:24
13:05:00
13:50:30
14:50:32
15:15:10
I want a function or query to check if '13:25:00' has '00:20:00' difference with any of the times in table.
The output should return:
13:05:00
Based on the information you have provided, I assume you want to get values from the list that are the specified period either side of your "special time".
Here's one way to do it using DATEADD:
-- temp table for your sample data
CREATE TABLE #times ( val TIME )
INSERT INTO #times
( val )
VALUES ( '12:45:24' ),
( '13:05:00' ),
( '13:50:30' ),
( '14:50:32' ),
( '15:15:10' )
DECLARE #special_time TIME = '13:25:00'
DECLARE #diff_value TIME = '00:20:00'
-- variable will hold the total number of seconds for your interval
DECLARE #diff_in_seconds INT
-- gets the total number of seconds of your interval -> #diff_value
SELECT #diff_in_seconds = DATEPART(SECOND, #diff_value) + 60
* DATEPART(MINUTE, #diff_value) + 3600 * DATEPART(HOUR, #diff_value)
-- get the values that match the criteria
SELECT *
FROM #times
WHERE val = DATEADD(SECOND, #diff_in_seconds, #special_time)
OR val = DATEADD(SECOND, -( #diff_in_seconds ), #special_time)
DROP TABLE #times
Note that the WHERE clause filters the results by adding and subtracting the difference. The subtraction is achieved by making the #diff_in_seconds negative.
If we are understanding your question correctly, you want all the times that are bigger than 20 minutes from your given (special) time.
To achieve this, just do a select with a where clause that contains a clause looking like this: abs(datediff(minute, tableDate, #specialdate)) > 20
SQLFiddle sample and code example:
declare #specialDate datetime = '1900-01-01 13:25:00'
select *
from SampleData
where abs(datediff(minute, SomeDate, #specialDate)) > 20
Note that I set the dates of the Datetime columns to 1900-01-01 as an obscure reference, adjust according to your settings.
You will need the ABS in the line to make sure that both variants of the resulting datediff are checked (It can either bring back 0, > 0 or < 0)
References:
MSDN: DATEDIFF
MSDN: ABS
Here is a solution:
create table t(t time);
insert into t
values
('12:45:24'),
('13:05:00'),
('13:50:30'),
('14:50:32'),
('15:15:10')
declare #st time = '13:25:00'
declare #dt time = '00:20:00'
select * from t
where abs(datediff(ss, t, #st)) - datediff(ss, '00:00:00', #dt) = 0
abs(datediff(ss, t, #st) will hold difference in seconds between times in table and special time. You compare this difference to difference between 00:00:00 and interval datediff(ss, '00:00:00', #dt)
Output:
t
13:05:00.0000000
Fiddle http://sqlfiddle.com/#!6/05df4/1

Convert Decimal values(hours) to Appropriate Hours in sql server 2008

I have a column called TimeSpent of type Float.
It contains values (i.e. 2.50, 1.25, 3.75, 5.60 )
I need to convert these hour values to appropriate hour values like (2:30, 1:15, 3:45 ,5:36) etc)
How can this be done?
Try this Query
select
time_decimal
,cast(cast(cast(time_decimal as int) as varchar)+
':'+cast(cast((time_decimal - cast(time_decimal as int)) * 60 as int) as varchar)+
':'+cast(cast(((time_decimal - cast(time_decimal as int)) * 60-
cast((time_decimal - cast(time_decimal as int)) * 60 as int)) * 60 as int) as varchar) as time) as real_time
from time1
SQL FIDDLE
Just do the math: the hour you can get by casting to int, for example. To get the minutes, multiply the decimal part by 60, etc.
A quick and dirty way to do it in a single query could be:
declare #d float
set #d = 1.54
select cast(#d as int) h,
cast((#d - cast(#d as int)) * 60 as int) m,
cast(((#d - cast(#d as int)) * 60
- cast((#d - cast(#d as int)) * 60 as int)) * 60 as int) s
Assuming you have created already a column with datatype time, you update your table by concatenating the left of TimeSpent till the dot and the part right of the dot multiplied by 60.
SQL Server 2012:
UPDATE yourTable SET
newTimeColumn = CONCAT(CAST(TimeSpentFloat AS INT), ':', (TimeSpentFloat - CAST(TimeSpentFloat AS INT)) * 60);
SQL Server 2008 and lower:
SELECT
CAST(CAST(2.75 AS INT) AS CHAR) + ':' + CAST((2.75 - CAST(2.75 AS INT)) * 60 AS CHAR);
See it working in an sqlfiddle.

SUM minutes SQL server

HI guys,
My litle problem goes like this :
I have this columns : PHONE_NR , TIME ( time field ), Meaning the calling telephone number and call duration. I need to group phone nr and sum the minutes. Filds looks like this :
nr time
726028xxx 00:07:07
735560css 00:07:37
726028xxx 00:07:55
SELECT PHONE_NR, SUM(DATEPART(minute, TIME)) FROM [table] GROUP BY PHONE_NR;
As far as I know this should work for both SQL Server DATETIME and the 2008 TIME data-types.
Example:
DECLARE #tbl TABLE
(
phone_nr varchar(10),
call_time TIME
);
INSERT INTO #tbl VALUES ('726028xxx', '00:07:07');
INSERT INTO #tbl VALUES ('735560css', '00:07:37');
INSERT INTO #tbl VALUES ('726028xxx', '00:07:55');
SELECT phone_nr, SUM(DATEPART(minute, call_time)) AS total_call_time FROM #tbl GROUP BY phone_nr;
Results in:
phone_nr | minutes
726028xxx | 14
735560css | 7
EDIT:
This version is identical to the above, except it also takes into account the seconds and hours (e.g. 1hr 7min 07secs = 67.117 minutes) so it's much more accurate.
SELECT
phone_nr,
SUM(CAST(DATEPART(second, call_time) + (DATEPART(minute, call_time) * 60) + (DATEPART(hour, call_time) * 3600) AS decimal) / 60) AS total_call_time
FROM
#tbl
GROUP BY
phone_nr;
Results in the following if the first record was 01:07:07.
phone_nr | minutes
726028xxx | 75.033332
735560css | 7.616666
You can use DATEDIFF to get the total number of minutes that have passed since time "zero"
SELECT
[PHONE_NR],
SUM(DATEDIFF(minute, '00:00:00', [TIME]))
FROM
[YourTable]
GROUP BY
[PHONE_NR]
declare #v1 varchar(50)
declare #v2 varchar(50)
set #v1='03:10'
set #v2='01:50'
--select cast(left(v1,2) as int) + cast(left(v2,2)as int)
select
cast(
cast(left(#v1,2) as int) + cast(left(#v2,2)as int)
+(cast(right(#v1,2) as int) + cast(right(#v2,2) as int))/60 as varchar)
+':'+cast(( cast(right(#v1,2) as int) + cast(right(#v2,2) as int))%60 as varchar)