【问题标题】:Calculating total time hh:mm:ss in sql在sql中计算总时间hh:mm:ss
【发布时间】:2015-10-12 21:27:55
【问题描述】:

我正在尝试计算餐桌上的总小时数。但是,当总时间超过 24 小时时,我会得到错误的结果。假设总小时数为 30,但此查询计算为 6。另一件事是,如果总时间少于 24 小时,它会给出正确的结果。 你能帮帮我吗?

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

【问题讨论】:

    标签: sql sql-server datetime datediff


    【解决方案1】:

    我相信我从Adam Machanic's sp_whoisactive 存储过程中获取了以下内容。

    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'
    

    【讨论】:

      【解决方案2】:

      假设我有一张喜欢的桌子 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

      【讨论】:

        【解决方案3】:

        您似乎已将日期和时间放在单独的列中,这使所需的算术变得复杂。因此,以下将 logindate 与 logintime 结合起来,对于注销数据也是如此。然后可以计算(每行)或聚合(如此处所示)以秒为单位的差异。

        我使用 CROSS APPLY 来执行大部分计算,这样做纯粹是为了允许在输出时通过别名 ca.duration 重用结果。我还尝试显示更简单的子查询来帮助跟踪逻辑。

        nb:我使用的最终显示逻辑与 Cory 在我之前的回答中使用的相同。

        SQL Fiddle

        MS SQL Server 2008 架构设置

        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')
        ;
        

        查询 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 |
        

        查询 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 |
        

        查询 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 |
        

        【讨论】:

          【解决方案4】:

          sql server 中的time 类型最多只能保存 24 小时。所以你需要转换成datetime,然后做一个计算转换成小时:分钟:秒。

          另外,你需要将你的年份包含在你的数据中,否则如果有人在年份更改之前登录,然后在之后退出,你会得到非常大的负差。

          这是一个示例...我使用了一个通用表格表达式来计算两个日期之间的秒数差异,然后它以您需要的格式报告总差异。

          ;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
          

          另外,如果您需要在分钟和小时前加上前导零,我会在最终选择中这样做:

          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
          

          【讨论】:

            猜你喜欢
            • 2015-03-08
            • 2017-07-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-11-18
            • 2021-10-13
            • 1970-01-01
            相关资源
            最近更新 更多