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

时间:2016-07-01 18:11:30

标签: sql sql-server tsql sql-server-2012

尝试构建查询。

输入按“rn”列中的rownumber排序,以“name”中的每个唯一值开头,并在“act”中定义给定的条目序列。在列'act'中,它在多次出现时保持两个值,> sleep<和>唤醒<。目标是为其中一个值的每个连续行集找到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

所需的输出:

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  

输入源不提供更多列。在这个例子中,每个子集中的成员数量可以非常高。

我尝试了不同的聚合方式,但都没有效果。我相信使用LEADLAGG并且进一步的欺骗可能会让我在那里,但这看起来非常不优雅。我认为区分每个子集是关键,即创建一个对其所有成员唯一的标识符。有了这个,minmax的汇总很简单。也许我错了。也许这是不可能的。也许是自我加入。也许是一个递归的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)

2 个答案:

答案 0 :(得分:3)

您希望找到相似行的连续组,然后进行聚合。我喜欢行数方法的不同之处:

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;

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

答案 1 :(得分:1)

这假设您在rn编号中没有任何空白,因此它不会再次计算。

with T2 as (
    select *, row_number() over (partition by name, act order by rn) as grp_rn
    from T
)
select name, act, min(startt) as startt, max(endd) as endd
from T2
group by name, act, rn - grp_rn
order by name, startt;

http://rextester.com/CCQJJ93990

这是一个典型的空白和岛屿查询。这里的关键是当你有一个行集群时,两个不同的编号将逐步增加,这意味着差异将是集群的常量。当您沿着列表工作时,这种差异会增加。