如何汇总用户在工作上花费的加班时间

How to sum overtime hours users spent on work

我有两个 SQL 服务器表 employee(empid, empname)employee_movements(empid, MoveDatetime),我按日期时间从指纹存储员工的操作,我想计算每个员工下班后从 03:30pm 直到第二天 6:00am

所以如果我有这个数据

| EmpID | MoveDateTime       |
|-------|--------------------|
| 1     | 01.01.2020 3:30pm  |
| 1     | 01.01.2020 5:30pm  |
| 1     | 01.01.2020 11:00pm |
| 1     | 02.01.2020 02:30am |
| 2     | 01.01.2020 4:00am  |
| 2     | 01.01.2020 10:15am |
| 1     | 02.01.2020 4:00pm  |
| 1     | 02.01.2020 5:00pm  |

我应该得到这个结果

| EmpID | Entrance           | Departure          | Actual_hours |
|-------|--------------------|--------------------|--------------|
| 1     | 01.01.2020 3:30PM  | 01.01.2020 5:30PM  | 2.00         |
| 1     | 01.01.2020 11:00PM | 02.01.2020 2:30Am  | 3.5          |
| 2     | 01.01.2020 4:00AM  | 01.01.2020 10:15PM | 7.25         |
| 1     | 02.01.2020 04:00am | 02.01.2020 5:00am  | 01.00        |

我试过这个代码

with cte as 
(
  SELECT *, ROW_NUMBER() OVER (PARTITION BY [EmpID], cast ([MoveDateTime] as Date) 
                               ORDER BY [MoveDateTime]) as rn
  FROM employee_movements t1 
)  
SELECT c1.EmpID, c1.rn,c2.rn,
       cast (c1.MoveDateTime as Date) as the_day,c1.MoveDateTime as enterance,c2.MoveDateTime as departure, cast (sum (DATEDIFF (S, c1.MoveDateTime, c2.MoveDateTime)/3600.00) as decimal(18,2)) as Actual_Hours
FROM cte c1
left JOIN cte c2
  ON 
c1.rn = c2.rn -1 AND 
c1.EmpID = c2.EmpID and c1.rn % 2 <> 0 
and (cast (c1.MoveDateTime as Date)=cast (c2.MoveDateTime as Date) ) or (c2.rn=1 and cast(dateadd(day,1,c1.MoveDateTime) as date)= cast(c2.MoveDateTime as Date) and (cast(c2.MoveDateTime as time) between '00:00:00' and '06:00:00')) 
AND c2.rn % 2 = 0
 where c2.rn is not null
GROUP BY c1.EmpID,c1.rn,c2.rn,
         cast (c1.MoveDateTime as Date) ,c1.MoveDateTime,c2.MoveDateTime

我从中得到了每个员工的进出和小时数,但是它被一天划分的问题不是 return 第二种情况,因为员工仍在工作并离开了第二天。

任何帮助将不胜感激,在此先感谢。

作为初学者:您可以使用 window 函数交错行以生成 in/out 对:

select *
from (
    select empid, movedatetime as dt_in,
            lead(movedatetime) over(partition by empid order by movedatetime) as dt_out,
            row_number() over(partition by empid order by movedatetime) rn
        from employee_movements 
) t
where rn % 2 = 1

然后我们需要计算每个日期范围的加班时间。 SQL 服务器不太擅长日期算法;如果您没有太多的行可以一次处理,最简单的方法可能是蛮力:枚举两个日期之间的所有分钟数,只计算那些不属于工作时间的。

所以:

with 
    data as (
        select empid, movedatetime as dt_in,
            lead(movedatetime) over(partition by empid order by movedatetime) as dt_out,
            row_number() over(partition by empid order by movedatetime) rn
        from employee_movements 
    ),
    cte as (
        select empid, dt_in, dt_out, dt_in as dt 
        from data 
        where rn % 2 = 1
        union all
        select empid, dt_in, dt_out, dateadd(minute, 1, dt) 
        from cte 
        where dateadd(minute, 1, dt) < dt_out
    )
select empid, dt_in, dt_out, 
    sum(
        case when convert(time, dt) >= '06:00:00' and convert(time, dt) < '15:30:00' 
        then 0 
        else 1 
    end) ot_minutes
from cte
group by empid, dt_in, dt_out
order by empid, dt_in
option (maxrecursion 0) 

Demo on DB Fiddlde:

empid | dt_in                   | dt_out                  | ot_minutes
----: | :---------------------- | :---------------------- | ---------:
    1 | 2020-01-01 15:30:00.000 | 2020-01-01 17:30:00.000 |        120
    1 | 2020-01-01 23:00:00.000 | 2020-01-02 02:30:00.000 |        210
    1 | 2020-01-02 16:00:00.000 | 2020-01-02 17:00:00.000 |         60
    2 | 2020-01-01 04:00:00.000 | 2020-01-01 22:15:00.000 |        525