【问题标题】:DATEDIFF in HH:MM:SS formatHH:MM:SS 格式的 DATEDIFF
【发布时间】:2013-01-04 22:02:48
【问题描述】:

给定一些包含开始时间和结束时间的数据,我需要根据小时、分钟、秒和平均长度来计算总长度。

例如,结果必须类似于 45:15:10,表示 45 小时 15 分 10 秒,或 30:07 表示 30 分 07 秒。

我们正在使用SQL Server 2008 R2,当时间超过24:59:59 时转换失败。知道我该怎么做吗?

有关信息,表中的列是IdStartDateTimeEndDateTime 等。我需要制作一份月度报告,其中包含当月的录音计数、这些记录的总长度以及平均长度。我想知道是否有一种简单的方法来执行所有这些操作。

【问题讨论】:

  • 将所有内容转换为秒,求和,然后手动转换为可读格式。

标签: sql sql-server sql-server-2008 date datetime


【解决方案1】:

您不应该转换为time - 它的目的是在单个 24 小时时钟上存储一个时间点,而不是一个持续时间或间隔(即使是一个单独限制为

DECLARE @d TABLE
(
  id INT IDENTITY(1,1), 
  StartDateTime DATETIME, 
  EndDateTime DATETIME
);

INSERT @d(StartDateTime, EndDateTime) VALUES 
(DATEADD(DAY, -2, GETDATE()), DATEADD(MINUTE, 15, GETDATE())),
(GETDATE()                  , DATEADD(MINUTE, 22, GETDATE())),
(DATEADD(DAY, -1, GETDATE()), DATEADD(MINUTE,  5, GETDATE())),
(DATEADD(DAY, -4, GETDATE()), DATEADD(SECOND, 14, GETDATE()));

;WITH x AS (SELECT id, StartDateTime, EndDateTime, 
  d = DATEDIFF(SECOND, StartDateTime, EndDateTime),
  a = AVG(DATEDIFF(SECOND, StartDateTime, EndDateTime)) OVER()
  FROM @d
)
SELECT id, StartDateTime, EndDateTime,
  [delta_HH:MM:SS] = CONVERT(VARCHAR(5), d/60/60)
  + ':' + RIGHT('0' + CONVERT(VARCHAR(2), d/60%60), 2)
  + ':' + RIGHT('0' + CONVERT(VARCHAR(2), d % 60), 2),
  [avg_HH:MM:SS] = CONVERT(VARCHAR(5), a/60/60)
  + ':' + RIGHT('0' + CONVERT(VARCHAR(2), a/60%60), 2)
  + ':' + RIGHT('0' + CONVERT(VARCHAR(2), a % 60), 2)
FROM x;

结果:

id  StartDateTime        EndDateTime          delta_HH:MM:SS  avg_HH:MM:SS
--  -------------------  -------------------  --------------  ------------
1   2013-01-19 14:24:46  2013-01-21 14:39:46  48:15:00        42:10:33
2   2013-01-21 14:24:46  2013-01-21 14:46:46   0:22:00        42:10:33
3   2013-01-20 14:24:46  2013-01-21 14:29:46  24:05:00        42:10:33
4   2013-01-17 14:24:46  2013-01-21 14:25:00  96:00:14        42:10:33

这不是您所要求的,因为它不会只显示 MM:SS 的增量 CASE 表达式来调整它:

;WITH x AS (SELECT id, StartDateTime, EndDateTime, 
  d = DATEDIFF(SECOND, StartDateTime, EndDateTime),
  a = AVG(DATEDIFF(SECOND, StartDateTime, EndDateTime)) OVER()
  FROM @d
)
SELECT id, StartDateTime, EndDateTime,
  [delta_HH:MM:SS] = CASE WHEN d >= 3600 THEN 
    CONVERT(VARCHAR(5), d/60/60) + ':' ELSE '' END
  + RIGHT('0' + CONVERT(VARCHAR(2), d/60%60), 2)
  + ':' + RIGHT('0' + CONVERT(VARCHAR(2), d % 60), 2),
  [avg_HH:MM:SS] = CASE WHEN a >= 3600 THEN 
    CONVERT(VARCHAR(5), a/60/60) + ':' ELSE '' END
  + RIGHT('0' + CONVERT(VARCHAR(2), a/60%60), 2)
  + ':' + RIGHT('0' + CONVERT(VARCHAR(2), a % 60), 2)
FROM x;

此查询将上述结果中第二行的增量列从0:22:00 更改为22:00

【讨论】:

    【解决方案2】:

    如果您想计算平均值,那么最好的方法是转换为秒或一天的分数。日分数在 SQL Server 中很方便,因为您可以执行以下操作:

    select avg(cast(endtime - starttime) as float)
    from t
    

    您可以使用反向转换将其转换回datetime

    select cast(avg(cast(endtime - starttime as float) as datetime)
    from t
    

    以您想要的格式获取时间的算术。 . .那是一种痛苦。您可以考虑在最终格式中包含天数,并使用:

    select right(convert(varchar(255), <val>, 120), 10)
    

    要使小时数超过 24,这是另一种方法:

    select cast(floor(cast(<val> as float)*24) as varchar(255))+right(convert(varchar(255), <val>, 120), 6)
    

    它使用convert 表示分钟和秒,应该在左边用0 填充。然后将小时作为单独的值附加。

    【讨论】:

      【解决方案3】:
      SELECT CONVERT(time, 
                     DATEADD(mcs, 
                             DATEDIFF(mcs, 
                                      '2007-05-07 09:53:00.0273335', 
                                      '2007-05-07 09:53:01.0376635'), 
                             CAST('1900-01-01 00:00:00.0000000' as datetime2)
                            )
                    )
      

      【讨论】:

        【解决方案4】:

        我稍微修改了 Avinash 的答案,因为如果差异太大,它可能会以错误结束。如果您只需要 HH:mm:ss 就足以像这样在秒级区分:

        SELECT CONVERT(time, 
          DATEADD(s, 
            DATEDIFF(s, 
              '2018-01-07 09:53:00', 
              '2018-01-07 11:53:01'), 
             CAST('1900-01-01 00:00:00.0000000' as datetime2)
           )
        )
        

        【讨论】:

          【解决方案5】:

          一种避免溢出的方法,可以在输出中包含天数,一直到毫秒:

          DECLARE @startDate AS DATETIME = '2018-06-01 14:20:02.100'
          DECLARE @endDate AS DATETIME = '2018-06-02 15:23:09.000'
          SELECT CAST(DATEDIFF(day,'1900-01-01', @endDate - @startDate) AS VARCHAR) +  'd ' + CONVERT(varchar(22), @endDate - @startDate, 114)
          

          上面会返回

          1d 01:03:06:900

          当然,您可以使用您选择的格式

          SQL 支持日期时间减法,它输出相对于 MIN 日期的新日期时间(例如 1900-01-01,您可能可以从某个系统变量中获取此值)这比 DATEDIFF 效果更好,因为 DATEDIFF 将为每个计算一个“越过日期部分边界”,即使经过的时间小于整个数据部分。这种方法的另一个好处是它允许您使用日期格式转换。

          【讨论】:

            【解决方案6】:

            从 SQL SERVER 2012 开始,您不需要使用 DATEDIFF 函数。你可以使用 FORMAT 函数来实现你想要的:

            SELECT
                FORMAT(CONVERT(TIME, [appoitment].[Start] - [appointment].[End]), N'hh\:mm') AS 'Duration'
            FROM
                [tblAppointment] (NOLOCK)
            

            【讨论】:

            • @AaronBertrand,谢谢!一个好的建议是在使用 FORMAT 函数检索数据之前,在子查询或 WITH 语句中选择您需要的所有数据。并且总是在比较中避免它......
            【解决方案7】:

            如果days 是(正)天数,例如0.5 12 小时,则使用此表达式将其格式化为适当的持续时间:

            CONVERT(varchar(9), FLOOR(days * 24)) + RIGHT(CONVERT(char(19), CAST(days AS datetime), 120), 6)
            

            粘贴时,Excel 最多可以理解 9999:59:59 的值。应用自定义格式:英文版[h]:mm:ss(荷兰语为[u]:mm:ss)。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2011-10-28
              • 1970-01-01
              • 1970-01-01
              • 2017-02-09
              • 2017-07-21
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多