如何汇总用户在工作上花费的加班时间
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)
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
我有两个 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)
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