Più i genitori ad albero (o il digramma) implementazione di sql server 2005
-
21-08-2019 - |
Domanda
Ho bisogno di implementare un multi-figlio albero (o il digramma) su SQL Server 2005.Ho letto diversi articoli, ma la maggior parte di loro utilizza singola imparentato alberi con un unica radice come la seguente.
-My PC
-Drive C
-Documents and Settings
-Program Files
-Adobe
-Microsoft
-Folder X
-Drive D
-Folder Y
-Folder Z
In questo, tutto deriva da un elemento radice (il Mio PC).
Nel mio caso, un bambino potrebbe avere più di 1 genitore, come la seguente:
G A
\ /
B
/ \
X C
/ \
D E
\ /
F
Così ho il seguente codice:
create table #ObjectRelations
(
Id varchar(20),
NextId varchar(20)
)
insert into #ObjectRelations values ('G', 'B')
insert into #ObjectRelations values ('A', 'B')
insert into #ObjectRelations values ('B', 'C')
insert into #ObjectRelations values ('B', 'X')
insert into #ObjectRelations values ('C', 'E')
insert into #ObjectRelations values ('C', 'D')
insert into #ObjectRelations values ('E', 'F')
insert into #ObjectRelations values ('D', 'F')
declare @id varchar(20)
set @id = 'A';
WITH Objects (Id, NextId) AS
( -- This is the 'Anchor' or starting point of the recursive query
SELECT rel.Id,
rel.NextId
FROM #ObjectRelations rel
WHERE rel.Id = @id
UNION ALL -- This is the recursive portion of the query
SELECT rel.Id,
rel.NextId
FROM #ObjectRelations rel
INNER JOIN Objects -- Note the reference to CTE table name (Recursive Join)
ON rel.Id = Objects.NextId
)
SELECT o.*
FROM Objects o
drop table #ObjectRelations
Che restituisce il seguente INSIEME:
Id NextId
-------------------- --------------------
A B
B C
B X
C E
C D
D F
E F
Risultato atteso SET:
Id NextId
-------------------- --------------------
G B
A B
B C
B X
C E
C D
D F
E F
Si noti che la relazione G->B è mancante, perché si chiede un oggetto di avvio (che non funziona per me, perché non conosco l'oggetto principale dall'inizio) e utilizzando come punto di partenza di ignorare il G->B relazione.
Quindi, questo codice non funziona nel mio caso, perché richiede un oggetto di avvio, che è evidente in un SINGOLO albero di genitore (sarà sempre l'oggetto principale).Ma in multi-albero di genitore, si potrebbe avere più di 1 oggetto "root" (come nell'esempio, G e sono la "radice" di oggetti, dove la radice è un oggetto che non hanno un genitore (antenato)).
Così mi sono bloccato qui...Ho la necessità di modificare la query per NON chiedere un oggetto di avvio e ricorsivamente attraversano l'intero albero.Non so se è possibile con il (Id, NextId) realizzazione di...può essere che ho bisogno di conservarla come un grafico che utilizza un qualche tipo di Incidenza matrice, matrice di adiacenza o qualsiasi altra cosa (vedi http://willets.org/sqlgraphs.html).
Qualsiasi aiuto?Cosa ne pensate ragazzi?La ringrazio molto per il vostro tempo =)
Evviva!
Fonti:Fonte 1 Sorgente 2 Fonte 3
Soluzione
Bene, ho finalmente si avvicinò con la seguente soluzione. E 'il modo che ho trovato per sostenere gli alberi multi-radice e digrammi anche in bicicletta.
create table #ObjectRelations
(
Id varchar(20),
NextId varchar(20)
)
/* Cycle */
/*
insert into #ObjectRelations values ('A', 'B')
insert into #ObjectRelations values ('B', 'C')
insert into #ObjectRelations values ('C', 'A')
*/
/* Multi root */
insert into #ObjectRelations values ('G', 'B')
insert into #ObjectRelations values ('A', 'B')
insert into #ObjectRelations values ('B', 'C')
insert into #ObjectRelations values ('B', 'X')
insert into #ObjectRelations values ('C', 'E')
insert into #ObjectRelations values ('C', 'D')
insert into #ObjectRelations values ('E', 'F')
insert into #ObjectRelations values ('D', 'F')
declare @startIds table
(
Id varchar(20) primary key
)
;WITH
Ids (Id) AS
(
SELECT Id
FROM #ObjectRelations
),
NextIds (Id) AS
(
SELECT NextId
FROM #ObjectRelations
)
INSERT INTO @startIds
/* This select will not return anything since there are not objects without predecessor, because it's a cyclic of course */
SELECT DISTINCT
Ids.Id
FROM
Ids
LEFT JOIN
NextIds on Ids.Id = NextIds.Id
WHERE
NextIds.Id IS NULL
UNION
/* So let's just pick anyone. (the way I will be getting the starting object for a cyclic doesn't matter for the regarding problem)*/
SELECT TOP 1 Id FROM Ids
;WITH Objects (Id, NextId, [Level], Way) AS
( -- This is the 'Anchor' or starting point of the recursive query
SELECT rel.Id,
rel.NextId,
1,
CAST(rel.Id as VARCHAR(MAX))
FROM #ObjectRelations rel
WHERE rel.Id IN (SELECT Id FROM @startIds)
UNION ALL -- This is the recursive portion of the query
SELECT rel.Id,
rel.NextId,
[Level] + 1,
RecObjects.Way + ', ' + rel.Id
FROM #ObjectRelations rel
INNER JOIN Objects RecObjects -- Note the reference to CTE table name (Recursive Join)
ON rel.Id = RecObjects.NextId
WHERE RecObjects.Way NOT LIKE '%' + rel.Id + '%'
)
SELECT DISTINCT
Id,
NextId,
[Level]
FROM Objects
ORDER BY [Level]
drop table #ObjectRelations
Potrebbe essere utile per qualcuno. E 'per me = P Grazie
Altri suggerimenti
Se si desidera utilizzare tutte radice oggetti a partire oggetti, si deve prima aggiornare i tuoi dati per includere informazioni circa gli oggetti della directory principale (e le foglie).Si dovrebbe aggiungere il seguente schema:
insert into #ObjectRelations values (NULL, 'G')
insert into #ObjectRelations values (NULL, 'A')
insert into #ObjectRelations values ('X', NULL)
insert into #ObjectRelations values ('F', NULL)
Naturalmente si potrebbe anche scrivere la vostra ancora di query in modo che si seleziona come nodi radice i record che hanno un Id
che non si presenta come un NextId
, ma questo è più facile.
A quel punto, modificare la vostra ancora di query in questo modo:
SELECT rel.Id,
rel.NextId
FROM #ObjectRelations rel
WHERE rel.Id IS NULL
Se si esegue questa query, vedrai che si ottiene un sacco di duplicati, un sacco di archi si verificano più volte.Questo è perché ora hanno due risultati di ancoraggio query e quindi l'albero è attraversato due volte.
Questo può essere risolto modificando l'istruzione select per questo (nota la DISTINCT
):
SELECT DISTINCT o.*
FROM Objects o
Se non volete fare gli inserti suggerite da Ronald, questo avrebbe fatto !.
WITH CTE_MultiParent (ID, ParentID)
AS
(
SELECT ID, ParentID FROM #ObjectRelations
WHERE ID NOT IN
(
SELECT DISTINCT ParentID FROM #ObjectRelations
)
UNION ALL
SELECT ObjR.ID, ObjR.ParentID FROM #ObjectRelations ObjR INNER JOIN CTE_MultiParent
ON CTE_MultiParent.ParentID = ObjR.Id
)
SELECT DISTINCT * FROM CTE_MultiParent