时间没有以正确的格式显示;额外单位

时间:2014-01-23 10:47:43

标签: sql tsql datetime sql-server-2008-r2 sql-server-2012

成功计算一个人已经工作的TotalTime和他已经完成的加班时间,如果总工作时间大于08:00但是如果不大于它的持续时间人员没有在加班列中工作但是现在超时列应该显示那个时间以适当的负面形式,例如如果有人从14:49工作到 15:20小时比Overtime列应该显示-00:29但我的列显示它像23:29,为什么23:而不是00:?我怎样才能得到正确的时间?

CODE

with times as (
SELECT    t1.EmplID
        , t3.EmplName
        , min(t1.RecTime) AS InTime
        , max(t2.RecTime) AS [TimeOut]
        , cast(min(t1.RecTime) as datetime) AS InTimeSub
        , cast(max(t2.RecTime) as datetime) AS TimeOutSub
        , t1.RecDate AS [DateVisited]
FROM  AtdRecord t1 
INNER JOIN 
      AtdRecord t2 
ON    t1.EmplID = t2.EmplID 
AND   t1.RecDate = t2.RecDate
AND   t1.RecTime < t2.RecTime
inner join 
      HrEmployee t3 
ON    t3.EmplID = t1.EmplID 
group by 
          t1.EmplID
        , t3.EmplName
        , t1.RecDate
)
SELECT EmplID
,EmplName
,InTime
,[TimeOut]
,[DateVisited]
,convert(char(5),cast([TimeOutSub] - InTimeSub as time), 108) totaltime
,convert(char(5), case when TimeOutSub - InTimeSub >= '08:01' then 
cast(TimeOutSub - dateadd(hour, 8, InTimeSub) as time) else cast(8 - (TimeOutSub - InTimeSub) as time) end, 108) as overtime
FROM times

2 个答案:

答案 0 :(得分:2)

原因是因为数据类型TIME应该存储时间而-00:29不是时间(即这一次不会在一天内发生)。

您要做的是以用于显示时间的通常格式显示显示持续时间,这与显示时间不同。这可以通过运行:

相当简单地显示出来
SELECT CAST('-00:27' AS TIME)

引发错误:

  

从字符串转换日期和/或时间时转换失败。

显示此内容的原因是00:00减去00:31是前一天的23:29

我个人会使用分钟进行所有计算,例如

SELECT  StartTime,
        EndTime,
        HoursWorked = DATEDIFF(MINUTE, StartTime, EndTime) / 60.0,
        MinutesOver = 480 - DATEDIFF(MINUTE, StartTime, EndTime)
FROM    (VALUES 
            (CAST('06:50' AS TIME), CAST('15:20' AS TIME)),
            (CAST('07:50' AS TIME), CAST('15:20' AS TIME))
        ) t (StartTime, EndTime);

给出了:

StartTime | EndTime  | HoursWorked | MinutesOver
----------+----------+-------------+------------
06:50:00  | 15:20:00 |     8.5     |    -30
07:50:00  | 15:20:00 |     7.5     |     30

然后,您可以在表示层中应用任何格式。


Aaron Bertrand确实在关于Choosing the wrong Data Type的文章中提到了这一点。它声明:

使用TIME存储时间

  

使用SQL Server 2008,可能很容易在使用新TIME数据类型定义的列中存储持续时间。此数据类型不是为了存储持续时间,而是实际上是一个时间点。尝试在此处存储持续时间的问题是,当您的持续时间超过24小时时会发生什么?将“持续时间”存储在两列 - StartTime和EndTime中会更有用。您可以添加可以计算的第三列,以分钟或秒计算持续时间或任何有意义的值。如果您有理由相信持续时间总是少于24小时并且不会越过任何午夜边界,那么您对StartTime和EndTime列的选择可能是TIME,但更可能的是它们应该是SMALLDATETIME或DATETIME。


<强>附录

我要对你的代码做的第一件事是删除CTE中的内连接:

FROM    AtdRecord t1 
        INNER JOIN AtdRecord t2 
            ON t1.EmplID = t2.EmplID 
            AND t1.RecDate = t2.RecDate
            AND t1.RecTime < t2.RecTime

这是毫无意义的,它所做的只是确保每个员工每天有多个记录,这可以通过having子句更好地实现。我也会give your tables meaningful aliases,有2个表并不是真的那么必要,但是当你有很多表时使用t1,t2,t3等会很快混淆。所以你的查询成为:

WITH Times AS
(   SELECT  emp.EmplID, 
            emp.EmplName,
            InTime = MIN(atd.RecTime),
            OutTime = MAX(atd.RecTime),
            TimeWorked = DATEDIFF(MINUTE, MIN(atd.RecTime), MAX(atd.RecTime)),
            OverTime = DATEDIFF(MINUTE, MIN(atd.RecTime), MAX(atd.RecTime)) - 480,
            [DateVisited] = atd.RecDate
    FROM    AtdRecord atd 
            INNER JOIN HrEmployee emp 
                ON atd.EmplID = emp.EmplID 
    GROUP BY emp.EmplID, emp.EmplName, atd.RecDate
    HAVING COUNT(atd.RecTime) > 1
)
SELECT  t.EmplID,
        t.EmplName,
        t.InTime,
        t.OutTime,
        t.DateVisited,
        t.TimeWorked,
        OverTime,
        FormattedTimeWorked = CONVERT(CHAR(5), DATEADD(MINUTE, t.TimeWorked, 0), 8),
        FormattedOverTime = CASE WHEN t.OverTime < 0 THEN '-' ELSE '' END +
                                CONVERT(CHAR(5), DATEADD(MINUTE, ABS(t.OverTime), 0), 8)
FROM    Times t;

<强> Simplified Example on SQL Fiddle

答案 1 :(得分:0)

因为你正在减去日期。

January 14th, 14:49 - January 14th, 15:20January 13th, 23:29。如果你考虑它就有意义。

改为使用SUBTIME()

SELECT SUBTIME('14:49', '15:20'); -> '-00:31:00'

编辑:

SUBTIME()在SQL-Server中不可用。我的错。但对于任何绊倒这个问题的人来说,这可能是有用的,所以我会留在这里。