【问题标题】:Using SQL to find continuous segments of event data (Gaps and Islands)使用 SQL 查找事件数据的连续片段(间隙和孤岛)
【发布时间】:2015-05-03 22:51:26
【问题描述】:

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

为了增加额外的复杂性,我们可能需要考虑一个容差(即一个事件在 1.15 结束,另一个从 1.2 开始可能被认为是一个连续事件。

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

这是行不通的,因为生产数据太“真实”,而且总是存在流程不适合的边缘情况。*

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

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

【问题讨论】:

  • 您使用的是 SQL Server 2012 或更高版本吗? 2012 年对窗口函数进行了很多改进,可能会对您有所帮助。
  • 很抱歉,2005 年它仍在运行。

标签: sql sql-server database statistics analysis


【解决方案1】:

找出缝隙和岛屿的一种方法是找到所有缝隙大到足以形成岛屿的地方,然后使用该信息来识别岛屿。这可能就是您所说的“反转”。

在您的情况下,请从以下内容开始:

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 作为逻辑,如果这对段进行排序。

【讨论】:

  • 谢谢 Gordon,这正是我所说的反转。我现在将对其进行测试。
【解决方案2】:

我会用表函数试试运气:

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;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-05
    • 1970-01-01
    • 2016-11-04
    • 2022-01-11
    • 1970-01-01
    • 2021-03-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多