Mrp*_*kin 2 sql-server window-functions running-totals
我的数据包含 ID、状态和时间 (hh:mm:ss)。
我需要计算每个“位置”和“隧道外”状态之间的总时间
ID || time || Status
________________________________________________
1 || 08:09:14 || Out of Tunnel
2 || 08:10:59 || Location
3 || 08:11:42 || Out of Tunnel
4 || 08:11:55 || Location
5 || 08:16:36 || Location
6 || 09:41:36 || Location
7 || 09:43:10 || Out of Tunnel
8 || 09:43:19 || Location
Run Code Online (Sandbox Code Playgroud)
“位置”状态标记我需要开始计数的位置。我需要在下次状态为“隧道外”时停止计数。如果没有给出相应的'Out of Tunnel',则不要计算它。
然后将每个总数相加以得出总计。例如:
______________________
| Total Time in Tunnel |
|______________________|
| |
| 01:32:11 |
|______________________|
Run Code Online (Sandbox Code Playgroud)
小智 7
从 ID 4 开始的第二轮在您的样本数据中的时间为 08:11:55,因此如果确实是您应该用作起点的时间,则总数应为 01:31:58。无论如何,这里有一个使用 LAG 和 LEAD 窗口函数的解决方案。如果要防止计划中的排序,请确保创建以下支持索引:
create unique index idx_time_status on dbo.t1(time, status);
Run Code Online (Sandbox Code Playgroud)
如果您在 2016+ 上运行,则可以通过创建以下虚拟索引来启用批处理:
create nonclustered columnstore index idx_cs on dbo.t1(id) where id = -1 and id = -2;
Run Code Online (Sandbox Code Playgroud)
在 SQL Server 2019 之前,如果查询中的至少一个参与表上不存在列存储索引,则 SQL Server 不会考虑使用批处理。创建这个虚拟索引,即使它本身确实没有意义,也可以为窗口函数使用更优化的批处理。检查有和没有索引的计划。我在这里详细解释了这一点。
这是解决方案代码:
with c1 as
(
select *,
case
when status = 'location'
and lag(status) over(order by time) = 'location' then 'no'
else 'yes'
end as keeper
from dbo.t1
where status in ('location', 'out of tunnel')
),
c2 as
(
select *, lead(time) over(order by time) as nxt
from c1
where keeper = 'yes'
)
select dateadd(second, sum(datediff(second, time, nxt)), cast('00:00:00' as time(0))) as total
from c2
where status = 'location';
Run Code Online (Sandbox Code Playgroud)
假设总时间少于 24 小时,我将输出格式化为 TIME。如果可以更多,您只需要添加一些格式化逻辑。
归档时间: |
|
查看次数: |
86 次 |
最近记录: |