查找连续行子集的最小值和最大值 - 间隙和孤岛

Der*_*r U 2 sql t-sql sql-server sql-server-2012

尝试构建查询。

输入按“rn”列中的行号排序,“name”中的每个唯一值从 1 开始,并在“act”中定义给定的条目序列。在“act”列中,它在多次出现时保存两个值,>sleep< 和>wake<。目标是为这些值之一的每个连续行集找到 startt 和 endd 的最小值和最大值。

这应是输入:

name       act        rn     startt endd
---------- ---------- ------ ------ ------
jimmy      sleep      1      1      3
jimmy      wake       2      4      7
jimmy      wake       3      8      10
jimmy      sleep      4      11     13
karen      wake       1      1      4
karen      sleep      2      5      7
karen      wake       3      8      9
karen      wake       4      10     12
karen      wake       5      13     14
karen      sleep      6      15     17
karen      sleep      7      18     20
Run Code Online (Sandbox Code Playgroud)

所需的输出:

name       act        startt endd   
---------- ---------- ------ ------ 
jimmy      sleep      1      3      
jimmy      wake       4      10     
jimmy      sleep      11     13     
karen      wake       1      4      
karen      sleep      5      7      
karen      wake       8      14     
karen      sleep      15     20  
Run Code Online (Sandbox Code Playgroud)

输入源不提供更多列。每个子集中的成员数量可能比本例中的要多得多。

我尝试了不同的聚合方法,但没有成功。我相信使用LEADLAGG进一步的欺骗可能会让我到达那里,但这似乎非常不优雅。我认为区分每个子集是关键,即创建一个对其所有成员唯一的标识符。有了这个,用min和进行聚合max就很简单了。也许我错了。也许这是不可能的。也许是自我加入。也许是递归 cte。我不知道。

那么:有人知道如何得到这个吗?非常感谢您的帮助。

更新:

感谢 Gordon Linoff、shawnt00 和其他发表评论的贡献者。有了你的建议,我感到我的逻辑关闭工具箱中存在重大差距。

对于有兴趣的人:

declare @t table (
    name nvarchar(10)
    ,act nvarchar (10)
    ,startt smallint
    ,endd smallint
    )

insert into @t (
    name
    ,act
    ,startt
    ,endd
    )
values
     ('jimmy','sleep', 1,3)
    ,('jimmy','wake', 4,7)
    ,('jimmy','wake', 8,10)
    ,('jimmy','sleep', 11,13)
    ,('karen','wake', 1,4)
    ,('karen','sleep', 5,7)
    ,('karen','wake', 8,9)
    ,('karen','wake', 10,12)
    ,('karen','wake', 13,14)
    ,('karen','sleep', 15,17)
    ,('karen','sleep', 18,20)

; --- all rows, no aggregating
with 
cte as (
select 
    name
    ,act
    ,row_number() over (partition by name order by name,startt) rn
    ,row_number() over (partition by name, act order by name,startt) act_n
    ,startt
    ,endd
from
    @t )
select
    name
    ,act
    ,startt
    ,endd
    ,rn
    ,act_n
    ,rn - act_n diff
from 
    cte
order by 
    name
    ,rn

;--- aggregating for the desired ouput
with 
cte as (
select 
    name
    ,act
    ,row_number() over (partition by name order by name,startt) rn
    ,row_number() over (partition by name, act order by name,startt) act_n
    ,startt
    ,endd
from 
    @t )
select
    name
    ,act
    ,min(startt) startt
    ,max(endd)   endd
    ,min(rn)     min_rn
    ,max(rn)     max_rn
from 
    cte
group by 
    name
    ,act
    ,rn - act_n
order by 
    name
    ,min(rn)
Run Code Online (Sandbox Code Playgroud)

Gor*_*off 6

您想要找到相似行的连续组,然后进行聚合。我喜欢行号方法的差异:

select name, act, min(startt) as startt, max(endd) as endd
from (select i.*,
             row_number() over (partition by name, act order by rn) as seqnum_na,
             row_number() over (partition by name order by rn) as seqnum_n
      from input i
     ) i
group by (seqnum_n - seqnum_na), name, act;
Run Code Online (Sandbox Code Playgroud)

您可以通过查看子查询的作用来了解其工作原理。