Eu escrevi uma resposta para uma pergunta semelhante há algum tempo:Como encontrar todos os subgrafos conectados de um gráfico não direcionado . Nessa questão eu usei o SQL Server. Veja essa resposta para uma explicação detalhada dos CTEs intermediários. Eu adaptei essa consulta ao Postgres.
Pode ser escrito de forma mais eficiente usando o recurso de matriz Postgres em vez de concatenar o caminho em um
text
coluna. WITH RECURSIVE
CTE_Idents
AS
(
SELECT old AS Ident
FROM identities
UNION
SELECT new AS Ident
FROM identities
)
,CTE_Pairs
AS
(
SELECT old AS Ident1, new AS Ident2
FROM identities
WHERE old <> new
UNION
SELECT new AS Ident1, old AS Ident2
FROM identities
WHERE old <> new
)
,CTE_Recursive
AS
(
SELECT
CTE_Idents.Ident AS AnchorIdent
, Ident1
, Ident2
, ',' || Ident1 || ',' || Ident2 || ',' AS IdentPath
, 1 AS Lvl
FROM
CTE_Pairs
INNER JOIN CTE_Idents ON CTE_Idents.Ident = CTE_Pairs.Ident1
UNION ALL
SELECT
CTE_Recursive.AnchorIdent
, CTE_Pairs.Ident1
, CTE_Pairs.Ident2
, CTE_Recursive.IdentPath || CTE_Pairs.Ident2 || ',' AS IdentPath
, CTE_Recursive.Lvl + 1 AS Lvl
FROM
CTE_Pairs
INNER JOIN CTE_Recursive ON CTE_Recursive.Ident2 = CTE_Pairs.Ident1
WHERE
CTE_Recursive.IdentPath NOT LIKE ('%,' || CTE_Pairs.Ident2 || ',%')
)
,CTE_RecursionResult
AS
(
SELECT AnchorIdent, Ident1, Ident2
FROM CTE_Recursive
)
,CTE_CleanResult
AS
(
SELECT AnchorIdent, Ident1 AS Ident
FROM CTE_RecursionResult
UNION
SELECT AnchorIdent, Ident2 AS Ident
FROM CTE_RecursionResult
)
,CTE_Groups
AS
(
SELECT
CTE_Idents.Ident
,array_agg(COALESCE(CTE_CleanResult.Ident, CTE_Idents.Ident)
ORDER BY COALESCE(CTE_CleanResult.Ident, CTE_Idents.Ident)) AS AllIdents
FROM
CTE_Idents
LEFT JOIN CTE_CleanResult ON CTE_CleanResult.AnchorIdent = CTE_Idents.Ident
GROUP BY CTE_Idents.Ident
)
SELECT AllIdents
FROM CTE_Groups
GROUP BY AllIdents
;
Eu adicionei uma linha
(7,X,Y)
aos seus dados de amostra. SQL Fiddle
Resultado
| allidents |
|--------------------|
| Lydia,Mary,Nancy |
| Albert,Bob,Charles |
| X,Y |
| Zoe |