:
DECLARE @Edges TABLE
(
ID INT ,
Node1 CHAR(1) ,
Node2 CHAR(1)
);
INSERT INTO @Edges
( Node1, Node2 )
VALUES ( 'A', 'B' ),
( 'B', 'C' ),
( 'D', 'E' );
WITH CTE_Nodes
AS ( SELECT Node1 AS Node
FROM @Edges
UNION
SELECT Node2 AS Node
FROM @Edges
),
CTE_Pairs
AS ( SELECT Node1 ,
Node2
FROM @Edges
WHERE Node1 <> Node2
UNION
SELECT Node2 AS Node1 ,
Node1 AS Node2
FROM @Edges
WHERE Node1 <> Node2
),
CTE_Recursive
AS ( SELECT CAST(CTE_Nodes.Node AS VARCHAR(8000)) AS AnchorNode ,
Node1 ,
Node2 ,
CAST(',' + Node1 + ',' + Node2 + ',' AS VARCHAR(8000)) AS NodePath ,
1 AS Lvl
FROM CTE_Pairs
INNER JOIN CTE_Nodes ON CTE_Nodes.Node = CTE_Pairs.Node1
UNION ALL
SELECT CTE_Recursive.AnchorNode ,
CTE_Pairs.Node1 ,
CTE_Pairs.Node2 ,
CAST(CTE_Recursive.NodePath + CTE_Pairs.Node2 + ',' AS VARCHAR(8000)) AS NodePath ,
CTE_Recursive.Lvl + 1 AS Lvl
FROM CTE_Pairs
INNER JOIN CTE_Recursive ON CTE_Recursive.Node2 = CTE_Pairs.Node1
WHERE CTE_Recursive.NodePath NOT LIKE CAST('%,'
+ CTE_Pairs.Node2 + ',%' AS VARCHAR(8000))
),
CTE_RecursionResult
AS ( SELECT AnchorNode ,
Node1 ,
Node2
FROM CTE_Recursive
),
CTE_CleanResult
AS ( SELECT AnchorNode ,
Node1 AS Node
FROM CTE_RecursionResult
UNION
SELECT AnchorNode ,
Node2 AS Node
FROM CTE_RecursionResult
)
SELECT Edges.Node1 ,
Edges.Node2 ,
DENSE_RANK() OVER ( ORDER BY CASE WHEN CA_Data.XML_Value IS NULL
THEN Edges.Node1
ELSE CA_Data.XML_Value
END ) AS Cluster
FROM @Edges Edges
CROSS APPLY ( SELECT CTE_CleanResult.Node + ','
FROM CTE_CleanResult
WHERE CTE_CleanResult.AnchorNode = Edges.Node1
ORDER BY CTE_CleanResult.Node
FOR
XML PATH('') ,
TYPE
) AS CA_XML ( XML_Value )
CROSS APPLY ( SELECT CA_XML.XML_Value.value('.',
'NVARCHAR(MAX)')
) AS CA_Data ( XML_Value );
SQL Fiddle DEMO