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