查找一天中花费的时间以及工作休息时间
Find time spent in a day along with work break taken
我的情况是我需要找出一些内部应用程序在办公室花费的总时间。
我有这样的示例数据:
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
这里的状态有不同的含义:
1-开始
2-暂停
3- 简历
4- 结束
表示员工在状态为 1 时在 ScanDateTime 开始工作。他们可以休息(状态 2)并回来继续工作(状态 3),状态 4 表示他们正在结束工作。
注意:工作时间可能会有多次休息。
预期输出:
EmployeeId StartTime EndTime BreakInMins
87008 2018-08-02 16:03:00.227 2018-08-02 20:06:00.000 14
我试图按照一些示例来计算预期的结果集,但没有帮助。
我找不到任何类似示例可用的此类示例。
如有任何帮助,我们将不胜感激。
尝试以下查询: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
你可以试试这个。
通过Status
在CTE
中创建一个row_number
,因为我们需要知道哪个暂停时间对应哪个恢复时间。然后 self join
在 CTE
通过 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);
查询 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
| EmployeeId | StartTime | EndTime | BreakInMins |
|------------|----------------------|--------------------------|-------------|
| 87008 | 2018-08-02T20:06:00Z | 2018-08-02T16:03:00.227Z | 14 |
编辑
如果您的数据中有不同的日期,您可以尝试此查询。只需按 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);
查询 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
| 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 |
我认为员工每天有多次休息时间,您可以在下面查看我还提供了 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
请试试这个。处理多个 breaks/employees 和案例,当中断仍在进行中或会话未完成时
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;
这是测试友好的脚本:script
我的情况是我需要找出一些内部应用程序在办公室花费的总时间。
我有这样的示例数据:
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
这里的状态有不同的含义:
1-开始 2-暂停 3- 简历 4- 结束
表示员工在状态为 1 时在 ScanDateTime 开始工作。他们可以休息(状态 2)并回来继续工作(状态 3),状态 4 表示他们正在结束工作。 注意:工作时间可能会有多次休息。
预期输出:
EmployeeId StartTime EndTime BreakInMins
87008 2018-08-02 16:03:00.227 2018-08-02 20:06:00.000 14
我试图按照一些示例来计算预期的结果集,但没有帮助。
我找不到任何类似示例可用的此类示例。
如有任何帮助,我们将不胜感激。
尝试以下查询: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
你可以试试这个。
通过Status
在CTE
中创建一个row_number
,因为我们需要知道哪个暂停时间对应哪个恢复时间。然后 self join
在 CTE
通过 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);
查询 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
| EmployeeId | StartTime | EndTime | BreakInMins |
|------------|----------------------|--------------------------|-------------|
| 87008 | 2018-08-02T20:06:00Z | 2018-08-02T16:03:00.227Z | 14 |
编辑
如果您的数据中有不同的日期,您可以尝试此查询。只需按 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);
查询 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
| 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 |
我认为员工每天有多次休息时间,您可以在下面查看我还提供了 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
请试试这个。处理多个 breaks/employees 和案例,当中断仍在进行中或会话未完成时
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;
这是测试友好的脚本:script