简体   繁体   中英

Calculating total time hh:mm:ss in sql

I am trying to calculate the total hours at the table. However when the total time is more than 24 hour I get wrong result. Lets say total hour is 30 but this query calculates it 6. The other thing is if the total time is less than 24 hours it gives the right result. Can you help me?

SELECT cast(dateadd(SECOND,sum(datediff(SECOND,CAST (s.LOGINHOUR + ':' + s.LOGINMIN + ':' + l.LOGINSEC as time), CAST (s.LOGOUTHOUR + ':' + s.LOGOUTMIN + ':' + s.LOGOUTSEC as time))), '1/1/1900') as time)
from openquery(S, '

            SELECT 
               floor(MOD(logindate,10000)/100) as loginMonth,
               MOD(logindate,100) as loginDay,
           floor(logintime/10000) as loginHour,
           floor(MOD(logintime,10000)/100) as loginMin ,
           floor(MOD(logintime,100)) as loginSec,

           floor(MOD(logoutdate,10000)/100) as logoutMonth,
           MOD(logoutdate,100) as logoutDay,
           floor(logouttime/10000) as logoutHour,
           floor(MOD(logouttime,10000)/100) as logoutMin,
           floor(MOD(logouttime,100)) as logoutSec


               FROM lgu00 
               WHERE (login between date1 AND  date2) 
               AND (logout between date1 AND  date2)


             ')s

I believe I took the following from Adam Machanic's sp_whoisactive stored procedure.

DECLARE @startDate DATETIME2(0) = '2015-01-01';
DECLARE @endDate DATETIME2(0) = '2015-01-02 12:30:01'

SELECT
    RIGHT( '0' + CONVERT( VARCHAR(6), DATEDIFF( SECOND, @startDate, @endDate ) / 3600 ), 2 )
    + ':' + RIGHT( '0' + CONVERT( VARCHAR(2), DATEDIFF( SECOND, @startDate, @endDate ) % 3600 / 60 ), 2 ) + ':' + RIGHT( '0' + CONVERT( VARCHAR(2),
    DATEDIFF( SECOND, @startDate, @endDate ) % 60 ), 2 ) AS 'hh:mm:ss'

The time type in sql server only holds up to a maximum of 24 hours. So you need to convert to datetime and then do a calculation to convert to hours:minutes:seconds.

Also, you need to include your year in your data, otherwise if someone logs in before the year changes and then logs out after, you will get a very large negative difference.

Here is an example... I used a common table expression to calculate the difference in seconds between the two dates, and then it reports on the total difference in the format you need.

;with cte_LogTimes as
(
SELECT 
    DATEDIFF(second,
        CAST(convert(varchar(2),s.LOGINMONTH) + '/' 
            + convert(varchar(2),s.LOGINDAY) + '/' 
            + convert(varchar(4),s.LOGINYEAR) + ' ' 
            + convert(varchar(2),s.LOGINHOUR) + ':' 
            + convert(varchar(2),s.LOGINMIN) + ':' 
            + convert(varchar(2),s.LOGINSEC)
        as datetime),
        CAST(convert(varchar(2),s.LOGOUTMONTH) + '/' 
            + convert(varchar(2),s.LOGOUTDAY) + '/' 
            + convert(varchar(4),s.LOGOUTYEAR) + ' ' 
            + convert(varchar(2),s.LOGOUTHOUR) + ':' 
            + convert(varchar(2),s.LOGOUTMIN) + ':' 
            + convert(varchar(2),s.LOGOUTSEC) 
        as datetime)
    ) seconds
from openquery(S, '
    SELECT 
        floor(@logindate/10000) as loginYear,
        floor(MOD(logindate,10000)/100) as loginMonth,
        MOD(logindate,100) as loginDay,
        floor(logintime/10000) as loginHour,
        floor(MOD(logintime,10000)/100) as loginMin ,
        floor(MOD(logintime,100)) as loginSec,
        floor(logoutdate/10000) as logoutYear,
        floor(MOD(logoutdate,10000)/100) as logoutMonth,
        MOD(logoutdate,100) as logoutDay,
        floor(logouttime/10000) as logoutHour,
        floor(MOD(logouttime,10000)/100) as logoutMin,
        floor(MOD(logouttime,100)) as logoutSec
    FROM lgu00 
    WHERE (login between date1 AND date2) 
    AND (logout between date1 AND date2)
    ')s
)
select convert(varchar(10),SUM(seconds)/3600) + ':'
    + convert(varchar(10),SUM(seconds) % 3600 / 60) + ':'
    + convert(varchar(10),SUM(seconds) % 60)
from cte_LogTimes

Also, if you need your minutes and hours to have leading zeros, this is how I would do that in the final select:

select convert(varchar(10),SUM(seconds)/3600) + ':'
    + replace(str(convert(varchar(10),SUM(seconds) % 3600 / 60),2),' ','0') + ':'
    + replace(str(convert(varchar(10),SUM(seconds) % 60),2),' ','0')
from cte_LogTimes

Assume I have a table likes MyTable: ID | Date_time SELECT A.Total_time/3600 AS 'Hour', (A.Total_time - (A.Total_time/3600)*3600)/60 AS 'Minutes', (A.Total_time - ((A.Total_time/3600)*3600) - ((A.Total_time - (A.Total_time/3600)*3600)/60)*60) AS 'Second' FROM ( SELECT SUM( DATEPART(SECOND, Date_time) + 60*DATEPART(MINUTE, Date_time) + 3600*DATEPART(HOUR, Date_time)) AS Total_time FROM Test_Per ) AS A MyTable: ID | Date_time SELECT A.Total_time/3600 AS 'Hour', (A.Total_time - (A.Total_time/3600)*3600)/60 AS 'Minutes', (A.Total_time - ((A.Total_time/3600)*3600) - ((A.Total_time - (A.Total_time/3600)*3600)/60)*60) AS 'Second' FROM ( SELECT SUM( DATEPART(SECOND, Date_time) + 60*DATEPART(MINUTE, Date_time) + 3600*DATEPART(HOUR, Date_time)) AS Total_time FROM Test_Per ) AS A

You appear to have placed your dates and times into separate columns which complicates the wanted arithmetic. So, the following combines the logindate with logintime and the same for logout data. Then a difference in seconds can be calculated (per row) or aggregated (as displayed here).

I have employed a CROSS APPLY to perform most of the calculations, this is done purely to allow re-use of the result by the alias ca.duration at output. I also attempt to display simpler subqueries to help trace the logic.

nb: I am using the same final display logic Cory used in his answer before mine.

SQL Fiddle

MS SQL Server 2008 Schema Setup :

CREATE TABLE lgu00 
    ([logindate] date, [logintime] time, [logoutdate] date, [logouttime] time)
;

INSERT INTO lgu00 
    ([logindate], [logintime], [logoutdate], [logouttime])
VALUES
    ('2015-10-05', '09:00:00', '2015-10-10', '11:01:10'),
    ('2015-10-07', '09:00:00', '2015-10-10', '12:02:01'),
    ('2015-10-06', '09:00:00', '2015-10-10', '13:03:20'),
    ('2015-10-08', '09:00:00', '2015-10-10', '14:04:02'),
    ('2015-10-09', '09:00:00', '2015-10-10', '15:05:30'),
    ('2015-10-10', '09:00:00', '2015-10-10', '16:06:03'),
    ('2015-10-11', '09:00:00', '2015-10-11', '17:07:40')
;

Query 1 :

select
        RIGHT('0' + CONVERT(VARCHAR(6), SUM(ca.duration) / 3600 ), 2 )
      + ':' 
      + RIGHT('0' + CONVERT(VARCHAR(2), SUM(ca.duration) % 3600 / 60 ), 2 ) 
      + ':' 
      + RIGHT('0' + CONVERT(VARCHAR(2), SUM(ca.duration) % 60 ), 2 ) 
        AS [hh:mm:ss]
    , SUM(ca.duration) AS [In Seconds]
FROM lgu00 t
  CROSS APPLY (
        SELECT DATEDIFF(second
                        , DATEADD(day,DATEDIFF(day,0,t.logindate),cast(t.logintime AS datetime))
                        , DATEADD(day,DATEDIFF(day,0,t.logoutdate),cast(t.logouttime AS datetime))
                       )
        ) ca (duration)
WHERE t.logindate >= '20150101' AND t.logoutdate < '20151101'

Results :

| hh:mm:ss | In Seconds |
|----------|------------|
| 95:29:46 |    1423786 |

Query 2 :

select
      login
    , logout
from lgu00
  cross apply (
        select
              dateadd(day,datediff(day,0,logindate),cast(logintime as datetime)) as login
            , dateadd(day,datediff(day,0,logoutdate),cast(logouttime as datetime)) as logout
        ) ca1
  cross apply (
        select datediff(second,ca1.login,ca1.logout) as duration
       ) ca2

Results :

|                     login |                    logout |
|---------------------------|---------------------------|
| October, 05 2015 09:00:00 | October, 10 2015 11:01:10 |
| October, 07 2015 09:00:00 | October, 10 2015 12:02:01 |
| October, 06 2015 09:00:00 | October, 10 2015 13:03:20 |
| October, 08 2015 09:00:00 | October, 10 2015 14:04:02 |
| October, 09 2015 09:00:00 | October, 10 2015 15:05:30 |
| October, 10 2015 09:00:00 | October, 10 2015 16:06:03 |
| October, 11 2015 09:00:00 | October, 11 2015 17:07:40 |

Query 3 :

select
      duration
from lgu00
  cross apply (
        select datediff(second
                        , dateadd(day,datediff(day,0,logindate),cast(logintime as datetime))
                        , dateadd(day,datediff(day,0,logoutdate),cast(logouttime as datetime))
                       )
        ) ca (duration)

Results :

| duration |
|----------|
|   439270 |
|   270121 |
|   360200 |
|   191042 |
|   108330 |
|    25563 |
|    29260 |

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM