Eu tenho uma tabela que contém uma coluna id, uma coluna pai e uma coluna filho. Então, para cada registro, eu sei qual registro vem antes dele e o que virá depois dele em um relacionamento em cadeia, mas do registro individual, não sei em que posição está dentro da cadeia nem qual é a primeira parte ou a última parte de a corrente.
Para fins de demonstração, suponha que temos a seguinte configuração. Percebo que há muitos problemas de design com esse tipo de configuração, mas é com isso que tenho que trabalhar.
CREATE TABLE Relationships (
ID VARCHAR(4),
ParentID VARCHAR(4),
ChildID VARCHAR(4)
)
-- Insert root entries with their children
INSERT INTO Relationships (ID, ParentID, ChildID)
VALUES ('0001', '', '0003'), ('0002', '', '0004')
-- Now add further entries for each relationship chain
INSERT INTO Relationships(ID, ParentID, ChildID)
VALUES('0003', '0001', '0005'), ('0005', '0003', '0006'), ('0006', '0005', '0007'), ('0007', '0006', ''),
('0004', '0002', '')
--Now we have two chains of 0001 -> 0003 -> 0005 -> 0006 -> 0007 and 0002 -> 0004
Com um CTE recursivo como abaixo, posso descobrir como todos os registros estão relacionados em sua cadeia e sua posição dentro da cadeia.
WITH RelationshipChain AS (
SELECT ID, ParentID, ChildID, 0 AS Seq, ID AS RootID
FROM Relationships WHERE ParentID = ''
UNION ALL
SELECT r2.ID, r2.ParentID, r2.ChildID, rc.Seq + 1 AS Seq, rc.RootID AS RootID
FROM Relationships r2
INNER JOIN RelationshipChain rc ON rc.ChildID = r2.ID
)
SELECT * FROM RelationshipChain
ORDER BY RootID, Seq
Para cerca de 2 milhões de registros, isso é executado em cerca de 30 segundos, o que é muito bom; no entanto, se eu tentar incluir também a parte final da cadeia, levará 4 vezes mais tempo para ser executado. Atualmente, estou fazendo assim:
WITH RelationshipChain AS (
SELECT ID, ParentID, ChildID, 0 AS Seq, ID AS RootID
FROM Relationships WHERE ParentID = ''
UNION ALL
SELECT r2.ID, r2.ParentID, r2.ChildID, rc.Seq + 1 AS Seq, rc.RootID AS RootID
FROM Relationships r2
INNER JOIN RelationshipChain rc ON rc.ChildID = r2.ID
)
SELECT *
FROM RelationshipChain rc
CROSS APPLY (
SELECT MAX(Seq) AS FinalSeq FROM RelationshipChain WHERE rc.RootID = RootID
) AS b
CROSS APPLY (
SELECT ID AS LastChild FROM RelationshipChain WHERE b.FinalSeq = seq AND rc.RootID = RootID
) AS c
ORDER BY RootID, Seq
Existe uma maneira de fazer isso de forma mais eficiente?
Não tenho seu conjunto de dados, então não posso testar se isso é melhor, mas parece melhor. Depois de construir as cadeias, nós as invertemos para encontrar o item 'mais filho' em cada cadeia e, em seguida, juntamos de volta à cadeia original.
Resultados com seus dados de amostra:
Os índices ajudarão no desempenho.