Sql server 在SQL Server中,如何对按重叠时间分组的持续时间求和
我正在尝试在SQLServer2008中创建一个存储过程 我有一个“计时”表(可能有数千条记录): 这显示了每个工作人员在每台机器上工作的时间 我想制作一个结果表如下:Sql server 在SQL Server中,如何对按重叠时间分组的持续时间求和,sql-server,Sql Server,我正在尝试在SQLServer2008中创建一个存储过程 我有一个“计时”表(可能有数千条记录): 这显示了每个工作人员在每台机器上工作的时间 我想制作一个结果表如下: MachineID | StaffQty | TotalMins 1 | 1 | 90 1 | 2 | 60 2 | 1 | 120 3 | 1 | 120 3 | 2 |
MachineID | StaffQty | TotalMins
1 | 1 | 90
1 | 2 | 60
2 | 1 | 120
3 | 1 | 120
3 | 2 | 30
这将显示每台机器有多少分钟只有一个人使用它,每台机器有多少分钟有两个人使用它等等
通常情况下,我会发布到目前为止我已经尝试过的东西,但我所有的尝试似乎都太遥远了,我认为没有什么意义。
显然,我非常感谢能有一个完整的解决方案,但我也希望能朝着正确的方向稍微推动一下。我想这回答了你的问题:
declare @t table (StaffID int, MachineID int, StartTime datetime2,FinishTime datetime2)
insert into @t(StaffID,MachineID,StartTime,FinishTime) values
(1,1,'2018-01-01T12:00:00','2018-01-01T14:30:00'),
(2,1,'2018-01-01T12:00:00','2018-01-01T13:00:00'),
(3,2,'2018-01-01T12:00:00','2018-01-01T12:30:00')
;With Times as (
select MachineID,StartTime as Time from @t
union
select MachineID,FinishTime from @t
), Ordered as (
select
*,
ROW_NUMBER() OVER (PARTITION BY MachineID ORDER BY Time) rn
from Times
), Periods as (
select
o1.MachineID,o1.Time as StartTime,o2.Time as FinishTime
from
Ordered o1
inner join
Ordered o2
on
o1.MachineID = o2.MachineID and
o1.rn = o2.rn - 1
)
select
p.MachineID,
p.StartTime,
MAX(p.FinishTime) as FinishTime,
COUNT(*) as Cnt,
DATEDIFF(minute,p.StartTime,MAX(p.FinishTime)) as TotalMinutes
from
@t t
inner join
Periods p
on
p.MachineID = t.MachineID and
p.StartTime < t.FinishTime and
t.StartTime < p.FinishTime
group by p.MachineID,p.StartTime
希望你能看到每个CTE都在做什么。如果一个人的
FinishTime
与另一个人在同一台机器上的StartTime
正好相等,则这可能无法准确地给出您正在寻找的结果。希望在真实数据中是罕见的。我想这回答了你的问题:
declare @t table (StaffID int, MachineID int, StartTime datetime2,FinishTime datetime2)
insert into @t(StaffID,MachineID,StartTime,FinishTime) values
(1,1,'2018-01-01T12:00:00','2018-01-01T14:30:00'),
(2,1,'2018-01-01T12:00:00','2018-01-01T13:00:00'),
(3,2,'2018-01-01T12:00:00','2018-01-01T12:30:00')
;With Times as (
select MachineID,StartTime as Time from @t
union
select MachineID,FinishTime from @t
), Ordered as (
select
*,
ROW_NUMBER() OVER (PARTITION BY MachineID ORDER BY Time) rn
from Times
), Periods as (
select
o1.MachineID,o1.Time as StartTime,o2.Time as FinishTime
from
Ordered o1
inner join
Ordered o2
on
o1.MachineID = o2.MachineID and
o1.rn = o2.rn - 1
)
select
p.MachineID,
p.StartTime,
MAX(p.FinishTime) as FinishTime,
COUNT(*) as Cnt,
DATEDIFF(minute,p.StartTime,MAX(p.FinishTime)) as TotalMinutes
from
@t t
inner join
Periods p
on
p.MachineID = t.MachineID and
p.StartTime < t.FinishTime and
t.StartTime < p.FinishTime
group by p.MachineID,p.StartTime
希望你能看到每个CTE都在做什么。如果一个人的
FinishTime
与另一个人在同一台机器上的StartTime
正好相等,则这可能无法准确地给出您正在寻找的结果。希望在真实数据中是罕见的。对于Sql server 2012+
请说明您的Sql server版本
使用其他示例数据尝试我的脚本。
如果不起作用,请发布其他示例数据
我认为我的脚本可以用于其他测试场景
create table #temp(StaffID int,MachineID int,StartTime datetime,FinishTime datetime)
insert into #temp VALUES
(1, 1,'01/01/2018 12:00','01/01/18 14:30')
,(2, 1,'01/01/2018 12:00','01/01/18 13:00')
,(3, 2,'01/01/2018 12:00','01/01/18 12:30')
;
WITH CTE
AS (
SELECT t.*
,t1.StaffQty
,datediff(MINUTE, t.StartTime, t.FinishTime) TotalMinutes
FROM #temp t
CROSS APPLY (
SELECT count(*) StaffQty
FROM #temp t1
WHERE t.machineid = t1.machineid
AND (
t.StartTime >= t1.StartTime
AND t.FinishTime <= t1.FinishTime
)
) t1
)
SELECT MachineID
,StaffQty
,TotalMinutes - isnull(LAG(TotalMinutes, 1) OVER (
PARTITION BY t.MachineID ORDER BY t.StartTime
,t.FinishTime
), 0)
FROM cte t
drop table #temp
对于
Sql server 2012+
请说明您的Sql server版本
使用其他示例数据尝试我的脚本。
如果不起作用,请发布其他示例数据
我认为我的脚本可以用于其他测试场景
create table #temp(StaffID int,MachineID int,StartTime datetime,FinishTime datetime)
insert into #temp VALUES
(1, 1,'01/01/2018 12:00','01/01/18 14:30')
,(2, 1,'01/01/2018 12:00','01/01/18 13:00')
,(3, 2,'01/01/2018 12:00','01/01/18 12:30')
;
WITH CTE
AS (
SELECT t.*
,t1.StaffQty
,datediff(MINUTE, t.StartTime, t.FinishTime) TotalMinutes
FROM #temp t
CROSS APPLY (
SELECT count(*) StaffQty
FROM #temp t1
WHERE t.machineid = t1.machineid
AND (
t.StartTime >= t1.StartTime
AND t.FinishTime <= t1.FinishTime
)
) t1
)
SELECT MachineID
,StaffQty
,TotalMinutes - isnull(LAG(TotalMinutes, 1) OVER (
PARTITION BY t.MachineID ORDER BY t.StartTime
,t.FinishTime
), 0)
FROM cte t
drop table #temp
非常感谢。我会看一看,看看我是否能理解它是如何工作的。如果其中一个范围完全包围了其他范围,这似乎不起作用。。。我将用额外的样本数据更新我的问题,以说明我的意思。@Gravitate-如果你只关心在每个“占用”级别的使用时间,那么你可以将当前的最终结果移动到CTE中,并进行最终查询
SUM(TotalMinutes)
和GROUP BY MachineID,Cnt
耶。。。我已经做到了。隐马尔可夫模型。。。我认为它不起作用,但通过我的示例数据检查,它确实起作用了。。。这一定是我做错了别的事情(或者是我没有想到的与我的实时数据有关的事情)。我会再打给你的。再次感谢你,好的。。。问题是,我删除了您的临时表,并将其替换为我的实际表。出于某种原因,当我在表的最终select中交换@t时出错。我知道这不是数据,就像我先将它插入临时表一样,没有问题。你知道为什么会这样吗?使用中间临时表可以解决问题,但如果不需要,我宁愿不使用它。再次感谢您迄今为止的帮助。谢谢。我会看一看,看看我是否能理解它是如何工作的。如果其中一个范围完全包围了其他范围,这似乎不起作用。。。我将用额外的样本数据更新我的问题,以说明我的意思。@Gravitate-如果你只关心在每个“占用”级别的使用时间,那么你可以将当前的最终结果移动到CTE中,并进行最终查询SUM(TotalMinutes)
和GROUP BY MachineID,Cnt
耶。。。我已经做到了。隐马尔可夫模型。。。我认为它不起作用,但通过我的示例数据检查,它确实起作用了。。。这一定是我做错了别的事情(或者是我没有想到的与我的实时数据有关的事情)。我会再打给你的。再次感谢你,好的。。。问题是,我删除了您的临时表,并将其替换为我的实际表。出于某种原因,当我在表的最终select中交换@t时出错。我知道这不是数据,就像我先将它插入临时表一样,没有问题。你知道为什么会这样吗?使用中间临时表可以解决问题,但如果不需要,我宁愿不使用它。再次感谢您迄今为止的帮助。谢谢。我来看看。另外,我在问题的第一行说明了我正在使用的SQL Server版本。没问题。我相信您的回答对任何使用更高版本SQL Server的有类似问题的人都会有帮助。@Gravitate,您可以查看我的SQL Server 2008版本。谢谢。我会看一看。谢谢你的帮助,但我选择了@Damien_作为不信者的答案,因为他先回答了,我发现它更容易理解。再次感谢,谢谢。我来看看。另外,我在问题的第一行说明了我正在使用的SQL Server版本。没问题。我相信您的回答对任何使用更高版本SQL Server的有类似问题的人都会有帮助。@Gravitate,您可以查看我的SQL Server 2008版本。谢谢。我会看一看。谢谢你的帮助,但我选择了@Damien_作为不信者的答案,因为他先回答了,我发现它更容易理解。再次感谢。
;
WITH CTE
AS (
SELECT t.*
,t1.StaffQty
,datediff(MINUTE, t.StartTime, t.FinishTime) TotalMinutes
,ROW_NUMBER() OVER (
PARTITION BY t.machineid ORDER BY t.StartTime
,t.FinishTime
) rn
FROM #temp t
CROSS APPLY (
SELECT count(*) StaffQty
FROM #temp t1
WHERE t.machineid = t1.machineid
AND (
t.StartTime >= t1.StartTime
AND t.FinishTime <= t1.FinishTime
)
) t1
)
SELECT t.MachineID
,t.StaffQty
,t.TotalMinutes - isnull(t1.TotalMinutes, 0) TotalMinutes
FROM cte t
OUTER APPLY (
SELECT TOP 1 TotalMinutes
FROM cte t1
WHERE t.MachineID = t1.machineid
AND t1.rn < t.rn
ORDER BY t1.rn DESC
) t1