SQL自我递归加入分组

时间:2017-09-29 17:14:17

标签: sql sql-server tsql

在SQL Server中,我有以下表格:

Name   New_Name 
---------------
A         B
B         C
C         D
G         H
H         I
Z         B

我想创建一个新表,链接与单个新groupID相关的所有名称

GroupID  Name
-------------
1         A
1         B
1         C
1         D
1         Z
2         G
2         H
2         I

我有点坚持这个可以解决除了一堆连接之外如何做到这一点。但我想做得恰到好处。

编辑问题以允许从两个不同的起点A和Z分组成一组。

2 个答案:

答案 0 :(得分:1)

由于你已经改变了问题,我正在更新答案。请注意,答案在逻辑结构方面是相同的。所有不同的是,在计算等级时,不是从G到I,现在回答从I到G进行计算。

Working demo link

;with cte as
( 
    select 
       t1.Name as Name, row_number() over (order by t1.Name) r,
       t1.New_Name as New_Name,
       1 as level 
    from yt t1 left join  yt t2
       on t1.New_Name=t2.name
    where t2.name is null
     union all
    select 
       yt.Name as Name, r,
       yt.New_Name as New_Name,
       c.level+1 as level
    from cte c  join yt
       on yt.New_Name=c.Name
   ),
   cte2 as 
   (   
     select r as group_id, Name from cte
     union
     select c1.r as group_id, c1.New_name as Name from cte c1
       where level = (select min(level) from cte c2 where c2.r=c1.r)
     )


 select * from cte2;
  

以下是旧答案。

您可以尝试以下基于CTE的查询:

create table yt (Name varchar(10),  New_Name varchar(10));
insert into yt values
('A','B'),
('B','C'),
('C','D'),
('G','H'),
('H','I');

;with cte as
( 
    select 
       t1.Name as Name, row_number() over (order by t1.Name) r,
       t1.New_Name as New_Name,
       1 as level 
    from yt t1 left join  yt t2
       on t1.Name=t2.New_name
    where t2.new_name is null
     union all
    select 
       yt.Name as Name, r,
       yt.New_Name as New_Name,
       c.level+1 as level
    from cte c  join yt
       on yt.Name=c.New_Name
   ),
 cte2 as 
 (   
   select r as group_id, Name from cte
   union
   select c1.r as group_id, c1.New_name as Name from cte c1
     where level = (select max(level) from cte c2 where c2.r=c1.r)
   )


 select * from cte2;

see working demo

答案 1 :(得分:0)

有点复杂但有效。

DECLARE @T TABLE  (Name VARCHAR(2),   New_Name VARCHAR(2))
INSERT INTO @T
VALUES
('A','B'),
('B','C'),
('C','D'),
('G','H'),
('H','I'),
('Z','B')

;WITH CTE AS
(
    SELECT * , RN = ROW_NUMBER() OVER(ORDER BY Name) FROM @T
)
,CTE2 AS (SELECT T1.RN, T1.Name Name1, T1.New_Name New_Name1, 
                 X.Name Name2, X.New_Name New_Name2, 
                 FLAG = CASE WHEN X.Name  IS NULL THEN 1 ELSE 0 END 
          FROM CTE T1
          OUTER APPLY (SELECT  * FROM CTE T2 WHERE T2.RN > T1.RN 
                       AND (T2.Name IN (T1.Name , T1.New_Name) 
                            OR T2.New_Name IN (T1.Name , T1.New_Name)
                       ))  AS X
)
,CTE3 AS (SELECT *, 
    GroupID = ROW_NUMBER() OVER (ORDER BY RN) -
          ROW_NUMBER() OVER (PARTITION BY Flag ORDER BY RN) +1
    FROM CTE2
)
SELECT 
    DISTINCT GroupID, Name 
FROM 
  (SELECT * FROM CTE3 WHERE Name2 IS NOT NULL) SRC 
  UNPIVOT ( Name FOR COL IN ([Name1], [New_Name1], [Name2], [New_Name2])) UNPVT

结果

GroupID              Name
-------------------- ----
1                    A
1                    B
1                    C
1                    D
1                    Z
2                    G
2                    H
2                    I