TSQL - 如何聚合数据集中出现的一种特定模式的时间戳间隔?
TSQL - How do I aggregate timestamp intervals for one particular pattern of occurence in a data set?
我有一项相当具有挑战性的任务,即从记录状态更改的数据集中构建查询,我需要在其中查找并聚合给定记录 ID 的两个不同状态之间的跨度,但该模式的出现是两者都发生变化且不一致。
但是,我无法想象以前没有这样做过。我正在寻找的是指向正确方向的指针,即应该使用什么 SQL 技术来提取此信息。
下面是数据集的示例:
id status datetime
1001 A 1/1/15 12:00 PM
1001 B 1/1/15 1:00 PM
1001 C 1/1/15 2:00 PM
1001 D 1/1/15 3:00 PM
1001 B 1/1/15 4:00 PM
1001 C 1/1/15 5:00 PM
1001 D 1/1/15 6:00 PM
1002 A 1/1/15 12:00 PM
1002 B 1/1/15 1:00 PM
1002 C 1/1/15 2:00 PM
1002 D 1/1/15 3:00 PM
1003 A 1/1/15 12:00 PM
1003 B 1/1/15 1:00 PM
1003 C 1/1/15 2:00 PM
1003 B 1/1/15 3:00 PM
1003 C 1/1/15 4:00 PM
1003 D 1/1/15 5:00 PM
1004 A 1/1/15 12:00 PM
1004 B 1/1/15 2:00 PM
1004 A 1/1/15 3:00 PM
1004 B 1/1/15 4:00 PM
1004 C 1/1/15 5:00 PM
1004 D 1/1/15 6:00 PM
在这种情况下,我试图找到每个记录 ID 的任何状态 B 到状态 C 更改之间的所有时间跨度的总和。如您所见,该模式有时会发生一次,有时从不发生,有时会发生多次,有时只会部分发生(例如 A 到 B 回到 A,这不会被计算在内)
所以从概念上讲,我正在寻找的输出看起来像这样:
id total b-c minutes
1001 120
1002 60
1003 120
1004 60
当然,我的实际数据并没有那么整齐地分成 1 小时的块。
与数据库人员相比,我更像是一名程序员。我可以在 C# 中轻松地执行某些迭代操作,但我正在尝试了解 SQL 中将使用哪些技术来执行相同的任务?
with TB as ( /* get the B rows and the timestamp of the next status */
select
id, status, tstamp,
(
select min(tstamp) from T as t2
where t2.id = t1.id and t2.tstamp > t1.tstamp
) as next_tstamp
from T as t1
where status = 'B'
)
select id, sum(datediff(ss, tstamp, next_tstamp)) /* or some other timespan function */
from TB as tb
where /* check that next status is a C. assumes tstamp is unique per id */
(select status from T where T.id = TB.id and T.tstamp = TB.next_tstamp) = 'C'
group by id
SQL Server 2008 选项 CROSS APPLY
select t1.id, sum(datediff(ss, t1.tstamp, t2.tstamp))
from
T as t1 cross apply
(
select top 1 status, tstamp /* using top is non-standard */
from T as t2
where t2.id = t1.id and t2.tstamp > t1.tstamp
order by tstamp desc
) as t2
where t1.status = 'B' and t2.status = 'C'
group by t1.id
这是使用窗口函数的一个很好的候选者。这是一种方法:
with
b_to_c_transitions as
(select id, status, datetime c_time,
lag(datetime) over (partition by id order by datetime) b_time
from logtable where status in ('B','C'))
select id, sum(datediff(minute, b_time, c_time))
from b_to_c_transitions where status='C' group by id
我有一项相当具有挑战性的任务,即从记录状态更改的数据集中构建查询,我需要在其中查找并聚合给定记录 ID 的两个不同状态之间的跨度,但该模式的出现是两者都发生变化且不一致。
但是,我无法想象以前没有这样做过。我正在寻找的是指向正确方向的指针,即应该使用什么 SQL 技术来提取此信息。
下面是数据集的示例:
id status datetime
1001 A 1/1/15 12:00 PM
1001 B 1/1/15 1:00 PM
1001 C 1/1/15 2:00 PM
1001 D 1/1/15 3:00 PM
1001 B 1/1/15 4:00 PM
1001 C 1/1/15 5:00 PM
1001 D 1/1/15 6:00 PM
1002 A 1/1/15 12:00 PM
1002 B 1/1/15 1:00 PM
1002 C 1/1/15 2:00 PM
1002 D 1/1/15 3:00 PM
1003 A 1/1/15 12:00 PM
1003 B 1/1/15 1:00 PM
1003 C 1/1/15 2:00 PM
1003 B 1/1/15 3:00 PM
1003 C 1/1/15 4:00 PM
1003 D 1/1/15 5:00 PM
1004 A 1/1/15 12:00 PM
1004 B 1/1/15 2:00 PM
1004 A 1/1/15 3:00 PM
1004 B 1/1/15 4:00 PM
1004 C 1/1/15 5:00 PM
1004 D 1/1/15 6:00 PM
在这种情况下,我试图找到每个记录 ID 的任何状态 B 到状态 C 更改之间的所有时间跨度的总和。如您所见,该模式有时会发生一次,有时从不发生,有时会发生多次,有时只会部分发生(例如 A 到 B 回到 A,这不会被计算在内)
所以从概念上讲,我正在寻找的输出看起来像这样:
id total b-c minutes
1001 120
1002 60
1003 120
1004 60
当然,我的实际数据并没有那么整齐地分成 1 小时的块。
与数据库人员相比,我更像是一名程序员。我可以在 C# 中轻松地执行某些迭代操作,但我正在尝试了解 SQL 中将使用哪些技术来执行相同的任务?
with TB as ( /* get the B rows and the timestamp of the next status */
select
id, status, tstamp,
(
select min(tstamp) from T as t2
where t2.id = t1.id and t2.tstamp > t1.tstamp
) as next_tstamp
from T as t1
where status = 'B'
)
select id, sum(datediff(ss, tstamp, next_tstamp)) /* or some other timespan function */
from TB as tb
where /* check that next status is a C. assumes tstamp is unique per id */
(select status from T where T.id = TB.id and T.tstamp = TB.next_tstamp) = 'C'
group by id
SQL Server 2008 选项 CROSS APPLY
select t1.id, sum(datediff(ss, t1.tstamp, t2.tstamp))
from
T as t1 cross apply
(
select top 1 status, tstamp /* using top is non-standard */
from T as t2
where t2.id = t1.id and t2.tstamp > t1.tstamp
order by tstamp desc
) as t2
where t1.status = 'B' and t2.status = 'C'
group by t1.id
这是使用窗口函数的一个很好的候选者。这是一种方法:
with
b_to_c_transitions as
(select id, status, datetime c_time,
lag(datetime) over (partition by id order by datetime) b_time
from logtable where status in ('B','C'))
select id, sum(datediff(minute, b_time, c_time))
from b_to_c_transitions where status='C' group by id