Find time spent in a day along with work break taken

雨燕双飞 提交于 2020-01-04 01:56:16

问题


I am in a situation where I need to find-out total time spent in office for some internal application.

I have sample data like this:

Id  EmployeeId  ScanDateTime    Status
 7  87008   2018-08-02 16:03:00.227 1
 8  87008   2018-08-02 16:06:17.277 2
 9  87008   2018-08-02 16:10:37.107 3
 10 87008   2018-08-02 16:20:17.277 2
 11 87008   2018-08-02 16:30:37.107 3
12  87008   2018-08-02 20:06:00.000 4

Here Status have different meanings:

1- Start 2- Pause 3- Resume 4- End

Means Employees start their work at ScanDateTime when status is 1. They can go for break(status 2) and come back and resume their work(Status 3) and with status 4 means they are ending their job. Note: There could be multiple breaks during work hours.

Expected Output:

EmployeeId  StartTime                 EndTime                  BreakInMins 
87008       2018-08-02 16:03:00.227   2018-08-02 20:06:00.000   14

I have tried to follow some example to calculate the expected result set but not helping.

I could not find any such example where this similar example available.

Any help would be appreciated.


回答1:


Please try this. Handles multiple breaks/employees and cases, when break is still in progress or session is not finished

select
     [EmployeeId]   =   [s].[EmployeeId]
    ,[StartTime]    =   [s].[ScanDateTime]
    ,[EndTime]      =   [et].[ScanDateTime]
    ,[BreakInMins]  =   [b].[BreakInMins]
from
    [Scans] as  [s] --  here is your table
outer apply
    (      
        select top 1 [ScanDateTime], [Id] from [Scans] where [Id] > [s].[Id] and [EmployeeId] = [s].[EmployeeId] and [Status] = 4 order by [ScanDateTime] asc
    )       as  [et]
outer apply
    (
        select
              [BreakInMins] = sum(isnull([r].[mins], datediff(mi, [sp].[ScanDateTime], getdate())))
        from
            [Scans] as [sp]
        outer apply
            (
                select top 1 [mins] = datediff(mi, [sp].[ScanDateTime], [ScanDateTime]) from [Scans] where [Id] > [sp].[Id] and [EmployeeId] = [sp].[EmployeeId] and [Status] IN (3, 4) order by [ScanDateTime] asc
            ) as [r]
        where
                [sp].[id] > [s].[id] and [sp].[id] < isnull([et].[id], [id] + 1)
            and [sp].[EmployeeId] = [s].[EmployeeId]
            and [sp].[Status] = 2

    )       as  [b]    
where
        [Status] = 1;

Here is test-friendly script: script




回答2:


i consider multiple breaks per day of employee you can check below i also provided fiddle link

select t1.*,t5.breakmins from
(
  select EmployeeId,min(StartTime) as StartTime,max(EndTime) as EndTime from 
(
  select EmployeeId,(case when status=1 then ScanDateTime end) as StartTime,
(case when status=4 then ScanDateTime end) as EndTime,
case when status=3 then ScanDateTime end as ResumeWork,
             case when status=2 then ScanDateTime end as pauseTime
    from emp

    ) as t group by EmployeeId
  )  t1
inner join
(
select EmployeeId, convert(date,ResumeWork) as day ,
 sum(case when status=2 then datediff(minute,ResumeWork,res)  end ) as breakmins from
(
select EmployeeId,ResumeWork,status ,
lag(ResumeWork) over(PARTITION BY EmployeeId order by ResumeWork desc) as res from 
(
 select * from 
(
select EmployeeId, case when status=3 then ScanDateTime end as ResumeWork,status from emp
) as t1 where ResumeWork is not null


    union all 
    select * from 
(
    select EmployeeId,case when status=2 then ScanDateTime end as pauseTime,status from emp
  ) as t2 where pauseTime is not null
 )  as t3 group by EmployeeId,ResumeWork,status
  ) t4 group by EmployeeId, convert(date,ResumeWork)
  )t5 on t1.EmployeeId=t5.EmployeeId
  and  convert(date,t1.StartTime)=t5.day


EmployeeId     StartTime                  EndTime          breakmins
87008   2018-08-02T16:03:00.227Z    2018-08-02T20:06:00Z    12

http://sqlfiddle.com/#!18/ae60f/6




回答3:


You can try this. make a row_number in CTE by Status, because we need to know which Pause time correspond which Resume time. then self join in the CTE by EmployeeId

CREATE TABLE T(
   Id  INT,
   EmployeeId INT,
   ScanDateTime DATETIME,
  Status INT
);


INSERT INTO T VALUES (7 ,87008 ,'2018-08-02 16:03:00.227',1);
INSERT INTO T VALUES (8 ,87008 ,'2018-08-02 16:06:17.277',2);
INSERT INTO T VALUES (9 ,87008 ,'2018-08-02 16:10:37.107',3);
INSERT INTO T VALUES (10,87008 ,'2018-08-02 16:20:17.277',2);
INSERT INTO T VALUES (11,87008 ,'2018-08-02 16:30:37.107',3);
INSERT INTO T VALUES (12,87008 ,'2018-08-02 20:06:00.000',4);

Query 1:

;with cte as(
  SELECT *,
         MIN(ScanDateTime) over(partition by EmployeeId order by EmployeeId) StartTime,
         MAX(ScanDateTime) over(partition by EmployeeId order by EmployeeId) EndTime,
         ROW_NUMBER() OVER(PARTITION BY Status order by id) rn
  FROM t
)
select t1.EmployeeId,
       t1.StartTime,
       t1.EndTime,
       SUM(datediff(minute,t1.ScanDateTime,t2.ScanDateTime)) BreakInMins 
from 
cte t1 
inner join cte t2
on t1.rn =t2.rn and t1.Status = 2 and t2.Status = 3 and t1.EmployeeId = t2.EmployeeId
group by t1.EmployeeId,
       t1.StartTime,
       t1.EndTime

Results:

| EmployeeId |            StartTime |                  EndTime | BreakInMins |
|------------|----------------------|--------------------------|-------------|
|      87008 | 2018-08-02T20:06:00Z | 2018-08-02T16:03:00.227Z |          14 |

EDIT

you can try this query if there are different day in your data. just group by the date.

CREATE TABLE T(
   Id  INT,
   EmployeeId INT,
   ScanDateTime DATETIME,
  Status INT
);


INSERT INTO T VALUES (7 ,87008 ,'2018-08-02 16:03:00.227',1);
INSERT INTO T VALUES (8 ,87008 ,'2018-08-02 16:06:17.277',2);
INSERT INTO T VALUES (9 ,87008 ,'2018-08-02 16:10:37.107',3);
INSERT INTO T VALUES (10,87008 ,'2018-08-02 16:20:17.277',2);
INSERT INTO T VALUES (11,87008 ,'2018-08-02 16:30:37.107',3);
INSERT INTO T VALUES (12,87008 ,'2018-08-02 20:06:00.000',4);
INSERT INTO T VALUES (27 ,87008 ,'2018-08-03 16:03:00.227',1);
INSERT INTO T VALUES (28 ,87008 ,'2018-08-03 16:06:17.277',2);
INSERT INTO T VALUES (29 ,87008 ,'2018-08-03 16:11:37.107',3);
INSERT INTO T VALUES (210,87008 ,'2018-08-03 16:20:17.277',2);
INSERT INTO T VALUES (211,87008 ,'2018-08-03 16:30:37.107',3);
INSERT INTO T VALUES (212,87008 ,'2018-08-03 20:06:00.000',4);

Query 1:

;with cte as(
  SELECT EmployeeId,
         MAX(CASE WHEN Status = 1 then ScanDateTime end) StartTime,
         MIN(CASE WHEN Status = 4 then ScanDateTime end) EndTime,
         CAST(ScanDateTime as date) dt
  FROM t
  GROUP BY EmployeeId,CAST(ScanDateTime as date)
)
,cte2 as(
  SELECT t2.*,
         Row_number() over(partition by t2.EmployeeId,t2.Status order by Id) rn,
         t1.StartTime,
         t1.EndTime,
         t1.dt
  FROM cte t1 
  INNER JOIN T t2 ON t1.EmployeeId = t2.EmployeeId and Status in (2,3) and t1.dt =  CAST(t2.ScanDateTime as date)
)
select t1.EmployeeId,
       t1.StartTime,
       t1.EndTime,
       SUM(datediff(minute,t1.ScanDateTime,t2.ScanDateTime)) BreakInMins 
from cte2 t1 
inner join cte2 t2 on 
  t1.rn = t2.rn 
and 
  t1.EmployeeId = t2.EmployeeId
and t1.Status = 2 and t2.Status =3
group by t1.EmployeeId,
       t1.StartTime,
       t1.EndTime

Results:

| EmployeeId |                StartTime |              EndTime | BreakInMins |
|------------|--------------------------|----------------------|-------------|
|      87008 | 2018-08-02T16:03:00.227Z | 2018-08-02T20:06:00Z |          14 |
|      87008 | 2018-08-03T16:03:00.227Z | 2018-08-03T20:06:00Z |          15 |



回答4:


Try below query: http://sqlfiddle.com/#!18/6fe11/3

   select id,min(case when status=1 then stattime end) as starttime,
min(case when status=4 then stattime end) as endtime,
sum(case when status=2 then minute end) as breakinmin
from
(
select id,stattime,status,
DATEdiff(minute,stattime,lead(stattime,1,NULL) 
         over (partition by id ORDER BY stattime)) as minute
    from ForgeRock)a
    group by id

id      starttime                   endtime                 breakinmin
87008   2018-08-02T16:03:00.227Z    2018-08-02T20:06:00Z    14


来源:https://stackoverflow.com/questions/51760743/find-time-spent-in-a-day-along-with-work-break-taken

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