2015-03-03 54 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;