我有一个数据集,我必须合并两个特定字段的范围。在研究过程中,我看到这个问题被称为“群岛和峡谷”。不幸的是,大多数IAG涉及在单个列(“StartDate”)之间进行合并。在我的问题中,我有两列构成一个范围(PlzVon-PlzBis)。
我已经找到了很多例子,但他们都倾向于在所有地方提到剥皮猫。他们中的一些使用CTE,其他人使用LAG / LEAD,这是我从未听说过的。
我尝试重新调整一个我发现的脚本,主要是因为我能理解它的内容,但没有骰子。我可以看到问题的所有“部分”(找到上/下端点,交配两个记录),但我无法弄清楚如何用它形成一个连贯的陈述。
在上面的照片中,我想组合突出显示的行,使它们是PlzVon-73000和PlzBis-74999。我可以说我需要一个CTE或一个相关的子查询,并且ON条件为b.PlzVon = a.PlzBis + 1.但是单个连接不够,因为新记录可以与另一个匹配,引导我们走上一条令人讨厌的递归和游标的道路。
任何有关如何合并这些岛屿的帮助都将非常感激。
答案 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