2015-03-03 53 views
0

我有一组具有开始和结束值的“事件”数据。数据可能会重叠或被端对端对接(最终值或一条记录匹配另一个的起始值)。我想找到一个很好的解决方案来查找事件的“岛屿”,即查找发生事件的跨度。下面的输入和预期输出表应该有助于可视化。使用SQL查找连续的事件数据段(间隙和岛屿)

要添加额外的复杂性,我们可能要考虑的公差(即一个事件在1.15结束和另一个开始在1.2可能被视为一个连续的事件。

我试图通过识别所有的来解决这个问题开始事件(那些事件没有从x事件结束开始,然后是结束事件,然后根据开始事件的起始值加上其下一个结束事件值来构建跨度)

这不起作用,因为生产数据太“真实”,并且总是存在处理不合适的附带案例。*

我目前的想法是,实际计算“差距”可能是理想的,然后反转这些差距以找到连续的事件跨度。

*请注意,实际生产数据非常大且非常复杂。有数百个很多事件由很小的碎片组成。

gaps and islands data

+0

你的SQL Server 2012或更高版本上? 2012年的窗口功能有很多改进可以帮助你。 – 2015-03-03 10:24:13

+0

我很抱歉地说,这仍然是在2005年运行。 – 2015-03-03 10:44:29

回答

0

一种方法的差距,和岛屿是要找到所有的地方有差距大到足以启动一个岛屿,然后使用该信息来识别这些岛屿的地方。这可能是你的意思是“反转”。

在你的情况,开始是这样的:

with e as ( -- This CTE gets the previous end value 
     select e.*, 
      (select max(endValue) 
       from events e2 
       where e2.StartValue < e.StartValue 
      ) as prev_endvalue 
     from events 
    ), 
    e2 as ( -- This CTE assigns a flag identifying the start of an island 
     select e.*, 
      (case when endValue - prevendValue < 0.1 then 0 else 1 end) as IslandStart 
     from e 
    ), 
    e3 as ( -- This CTE counts the number of island starts before each record, so all records in the island have the same value 
     select e2.*, 
      (select sum(IslandStart) 
       from e2 
       where e2.startValue <= e.startValue 
      ) as IslandId 
     from e2 
    ) 
select IslandId, min(startValue) as startValue, max(endValue) as endValue) 
from e3 
group by IslandId; 

在E2的定义0.5的值是边界条件的门槛。如果StartValues可以重复,代码可能需要一些调整。在这种情况下,最简单的解决方案是使用id作为逻辑,如果这样就对这些段进行排序。

+0

谢谢戈登,这正是我的意思是反转。我现在将把这个测试。 – 2015-03-03 13:12:03

0

我会尽我的运气与表功能:

create function fn_foobar() 
returns @Return table 
(id int primary key 
, sv float 
, ev float 
, unique (sv,ev) 
) as 
begin 
    insert into @Return 
    select d.id, d.sv, d.ev 
    from events d left join events f on f.sv<d.sv and d.sv<=f.ev 
    where f.id is null; 

    declare @rowcnt int = 1; 

    while (@rowcnt>0) 
    begin 
    update @Return 
    set ev=n.ev 
    from (select r.id, max(c.ev) ev 
      from @Return r join events c on c.sv<=r.ev and r.ev<c.ev 
      group by r.id 
     ) n 
    where n.id=[@Return].id; 
    set @rowcnt = @@ROWCOUNT; 
    end; 
return; 
end;