How to sum overtime hours users spent on work

老子叫甜甜 提交于 2020-12-26 12:13:06

问题


I have two SQL Server tables employee(empid, empname) and employee_movements(empid, MoveDatetime) which I store employees actions from fingerprint by datetime, I want to count hours every employee spent after work from 03:30pm until 6:00am in the next day

So if I have this data

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

I should get this result

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

I tried this code

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

Which I got from it the in and out of every employee and count of hours, but the problem it is partitioned by one day doesn't return the second case because the employee still in work and left on the next day.

Any help will be appreciated, thanks in advance.


回答1:


As a starter: you can interleave the rows using window functions to generate the in/out pairs:

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

Then we need to comput the overtime for each date range. SQL Server is not that good at date arithmetics; if you don't have too many rows to proceed at once, the simplest approach might be brute force: enumerate all the minutes between the two dates, the count only those that do not belong to the working hours.

So:

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


来源:https://stackoverflow.com/questions/64722173/how-to-sum-overtime-hours-users-spent-on-work

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!