T-SQL:群岛和差距,开始/结束

时间:2016-10-11 22:03:19

标签: tsql subquery sql-server-2014 gaps-and-islands

我有一个数据集,我必须合并两个特定字段的范围。在研究过程中,我看到这个问题被称为“群岛和峡谷”。不幸的是,大多数IAG涉及在单个列(“StartDate”)之间进行合并。在我的问题中,我有两列构成一个范围(PlzVon-PlzBis)。

我已经找到了很多例子,但他们都倾向于在所有地方提到剥皮猫。他们中的一些使用CTE,其他人使用LAG / LEAD,这是我从未听说过的。

我尝试重新调整一个我发现的脚本,主要是因为我能理解它的内容,但没有骰子。我可以看到问题的所有“部分”(找到上/下端点,交配两个记录),但我无法弄清楚如何用它形成一个连贯的陈述。 Example

在上面的照片中,我想组合突出显示的行,使它们是PlzVon-73000和PlzBis-74999。我可以说我需要一个CTE或一个相关的子查询,并且ON条件为b.PlzVon = a.PlzBis + 1.但是单个连接不够,因为新记录可以与另一个匹配,引导我们走上一条令人讨厌的递归和游标的道路。

任何有关如何合并这些岛屿的帮助都将非常感激。

2 个答案:

答案 0 :(得分:1)

如果我正确理解了这个问题,那么这是一个数据岛问题,但是在范围而不是关键字上。以下查询演示了如何使用数据岛方法解决此问题。

DECLARE @SourceData TABLE 
(
     ID         INT
    ,PlzVon     INT
    ,PlzBis     INT
)
INSERT INTO @SourceData 
VALUES
(1,38000,38999),
(2,73000,73999),
(3,74000,74999),
(4,75000,75999),
(5,85000,85999);


;WITH CTE_DataIslands  -- First CTE determine the start of each new data island
AS
(
    SELECT           [Main].ID
                    ,[Main].[PlzVon]
                    ,[Main].[PlzBis]
                    ,(
                        CASE
                            WHEN (LAG([Main].[PlzBis], 1) OVER  (ORDER BY [Main].[PlzVon] ASC) + 1 ) <> ([Main].[PlzVon]) THEN 1 -- If prev record's value for ([PlzBis] + 1)  is not equal to current record [PlzVon] value then it is the start of a new data island.
                            ELSE 0
                        END
                     ) AS [IsNewDataIsland]
    FROM            @SourceData [Main]
), CTE_GenerateGroupingID
AS
(
    SELECT  ID
            ,[PlzVon]
            ,[PlzBis]
            ,SUM([IsNewDataIsland]) OVER (ORDER BY [PlzVon] ROWS UNBOUNDED PRECEDING) AS GroupingID -- Create a running total of the IsNewDataIsland column this will create a grouping id we can now group on
    FROM    CTE_DataIslands
)
SELECT      MIN([PlzVon]) AS [PlzVon]       -- Min [PlzVon] will give the lower range
            ,MAX([PlzBis]) AS [PlzBis]      -- Max [PlzBis] will give the upper range (use min or max for any other column that should be included in the return result)
FROM        CTE_GenerateGroupingID
GROUP BY    GroupingID

答案 1 :(得分:0)

我不清楚PK是什么,所以我在样本表中添加了一个ID

Declare @YourTable Table (ID int,PlzVon int,PlzBis int)
Insert Into @YourTable values
(1,38000,38999),
(1,73000,73999),
(1,74000,74999)

;with cte0(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N))
     ,cteN(N) As (Select Top (Select max(PlzBis)-min(PlzVon)+1 From @YourTable) 
                         N=(Select min(PlzVon)-1 from @YourTable)+Row_Number() over (Order By (Select NULL)) 
                   From  cte0 N1, cte0 N2, cte0 N3, cte0 N4, cte0 N5, cte0 N6) 
     ,cteBase As (Select A.ID
                        ,A.PlzVon
                        ,A.PlzBis
                        ,PosNr = N.N
                        ,RowNr = N.N - Row_Number() Over (Partition By A.ID Order By N.N) 
                   From  cteN N
                   Join  @YourTable A on N.N Between A.PlzVon and A.PlzBis
                 )
Select ID
      ,PlzVon = min(PosNr)
      ,PlzBis = max(PosNr)
 From  cteBase
 Group By ID,RowNr
 Order By ID,min(PosNr)

返回

ID  PlzVon  PlzBis
1   38000   38999
1   73000   74999