2014-04-18 29 views
4

我有一個簡單的數據,在出現這樣的SQL - 整合重疊的數據

**ROW Start End** 
    0  1  2 
    1  3  5 
    2  4  6 
    3  8  9 

圖形SQL服務器設置,數據會出現這樣的 enter image description here

我想達成什麼是摺疊重疊的數據,以便我的查詢返回

**ROW Start End** 
    0  1  2 
    1  3  6 
    2  8  9 

這可能在SQL Server中,而無需編寫複雜的過程或統計信息EMENT?

+0

您使用的是哪個版本的SQL Server? –

+0

大量使用視覺效果,有助於我們理解並回答問題。 – JBrooks

+0

@GordonLinoff - SQL Server 2012 – jamesamuir

回答

2

下面是另一種替代方案的SQL Fiddle

首先,所有的限制按順序排序。然後刪除重疊範圍內的「重複」限制(因爲開始後是另一個開始或結束後面是另一個結束)。現在,範圍已摺疊,開始和結束值再次寫入同一行。

with temp_positions as --Select all limits as a single column along with the start/end flag (s/e) 
(
    select startx limit, 's' as pos from t 
    union 
    select endx, 'e' as pos from t 
) 
, ordered_positions as --Rank all limits 
(
    select limit, pos, RANK() OVER (ORDER BY limit) AS Rank 
    from temp_positions 
) 
, collapsed_positions as --Collapse ranges (select the first limit, if s is preceded or followed by e, and the last limit) and rank limits again 
(
    select op1.*, RANK() OVER (ORDER BY op1.Rank) AS New_Rank 
    from ordered_positions op1 
    inner join ordered_positions op2 
    on (op1.Rank = op2.Rank and op1.Rank = 1 and op1.pos = 's') 
    or (op2.Rank = op1.Rank-1 and op2.pos = 'e' and op1.pos = 's') 
    or (op2.Rank = op1.Rank+1 and op2.pos = 's' and op1.pos = 'e') 
    or (op2.Rank = op1.Rank and op1.pos = 'e' and op1.Rank = (select max(Rank) from ordered_positions)) 
) 
, final_positions as --Now each s is followed by e. So, select s limits and corresponding e limits. Rank ranges 
(
    select cp1.limit as cp1_limit, cp2.limit as cp2_limit, RANK() OVER (ORDER BY cp1.limit) AS Final_Rank 
    from collapsed_positions cp1 
    inner join collapsed_positions cp2 
    on cp1.pos = 's' and cp2.New_Rank = cp1.New_Rank+1 
) 
--Finally, subtract 1 from Rank to start Range #'s from 0 
select fp.Final_Rank-1 seq_no, fp.cp1_limit as starty, fp.cp2_limit as endy 
from final_positions fp; 

您可以測試每個CTE的結果並追蹤進展。例如,您可以通過刪除以下CTE並從上一個中選擇,如下所示。

with temp_positions as --Select all limits as a single column along with the start/end flag (s/e) 
(
    select startx limit, 's' as pos from t 
    union 
    select endx, 'e' as pos from t 
) 
, ordered_positions as --Rank all limits 
(
    select limit, pos, RANK() OVER (ORDER BY limit) AS Rank 
    from temp_positions 
) 
select * 
from ordered_positions; 
+0

此查詢已經過更多的數據測試(範圍5完全在範圍4內)並且可以正常工作。 SQL小提琴:http://www.sqlfiddle.com/#!6/95a11/11 –

+0

很高興爲你工作:-) –

1

這樣做的關鍵是爲重疊的片段分配一個「分組」值。然後,您可以通過此列進行彙總,以獲取所需的信息。段與其他段不重疊時啓動一個段。

with starts as (
     select t.*, 
      (case when exists (select 1 from table t2 where t2.start < t.start and t2.end >= .end) 
        then 0 
        else 1 
       end) as isstart 
     from table t 
    ), 
    groups as (
     select s.*, 
      (select sum(isstart) 
       from starts s2 
       where s2.start <= s.start 
      ) as grouping 
     from starts s 
    ) 
select row_number() over (order by min(start)) as row, 
     min(start) as start, max(end) as end 
from groups 
group by grouping; 
0

我想創建一個表值函數,返回段。然後,你會調用它:

select * 
from dbo.getCollapsedSegments(2, 9) 

下面是一個例子(我換成FIN END,因爲END是一個保留字。)

CREATE FUNCTION dbo.getCollapsedSegments(@Start int, @Fin int) 
RETURNS @CollapsedSegments TABLE 
(
    -- Columns returned by the function 
    start int, 
    fin int 
) 
AS 
BEGIN 

    SELECT @Start = (SELECT MIN(Start) FROM data WHERE @Start <= Start) 

    WHILE (@Start IS NOT NULL AND @Start < @Fin) 
    BEGIN 
     INSERT INTO @CollapsedSegments 
     SELECT MIN(s1.Start), MAX(ISNULL(s2.Fin, s1.Fin)) 
     FROM data s1 
     LEFT JOIN data s2 
     ON s1.Start < s2.Fin 
     AND s2.Start <= s1.Fin 
     AND @Fin > s2.start 
     WHERE s1.Start <= @Start 
     AND @Start < s1.Fin 

     SELECT @Start = (SELECT MAX(Fin) FROM @CollapsedSegments) 

     SELECT @Start = MIN(Start) 
     FROM data 
     WHERE Start > @Start 
    END 

    RETURN; 
END 

我的測試數據:

create table data 
(start int, 
fin int) 

insert into data 
select 1, 2 
union all 
select 3, 5 
union all 
select 4, 6 
union all 
select 8, 9 
union all 
select 10, 11