成功计算一个人已经工作的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
答案 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;
答案 1 :(得分:0)
因为你正在减去日期。
January 14th, 14:49
- January 14th, 15:20
是January 13th, 23:29
。如果你考虑它就有意义。
改为使用SUBTIME():
SELECT SUBTIME('14:49', '15:20'); -> '-00:31:00'
编辑:
SUBTIME()
在SQL-Server中不可用。我的错。但对于任何绊倒这个问题的人来说,这可能是有用的,所以我会留在这里。