Den lösning jag föreslår här använder begreppet materialiserad väg. Följande är ett exempel på materialiserade sökvägar som använder dina exempeldata. Jag hoppas att det hjälper dig att förstå det materialiserade vägkonceptet:
+----+--------------------------+----------+------------------+
| ID | Name | ParentID | MaterializedPath |
+----+--------------------------+----------+------------------+
| 1 | Parent 1 | 0 | 1 |
| 2 | Parent 2 | 0 | 2 |
| 4 | Parent 2 Child 1 | 2 | 2.4 |
| 6 | Parent 2 Child 1 Child 1 | 4 | 2.4.6 |
| 7 | Parent 2 Child 1 Child 2 | 4 | 2.4.7 |
| 3 | Parent 1 Child 1 | 1 | 1.3 |
| 5 | Parent 1 Child 1 Child | 3 | 1.3.5 |
+----+--------------------------+----------+------------------+
Varje nod N
har en materialiserad väg, den här vägen talar om för dig hur du ska gå från rotnoden till noden N
. Det kan byggas samman med nod-id:n. Till exempel för att nå noden 5
från dess rotnod besöker du nod 1
, nod 3
och nod 5
, så nod 5
materialiserad sökväg är 1.3.5
Av en slump kan den ordning du letar efter uppnås genom den materialiserade vägen.
I det föregående exemplet är materialiserade vägar sammanlänkade strängar, men jag föredrar binär sammanlänkning av ett antal anledningar.
För att bygga de materialiserade vägarna behöver du följande rekursiva CTE:
CREATE TABLE Tree
(
ID int NOT NULL CONSTRAINT PK_Tree PRIMARY KEY,
Name nvarchar(250) NOT NULL,
ParentID int NOT NULL,
)
INSERT INTO Tree(ID, Name, ParentID) VALUES
(1, 'Parent 1', 0),
(2, 'Parent 2', 0),
(3, 'Parent 1 Child 1', 1),
(4, 'Parent 2 Child 1', 2),
(5, 'Parent 1 Child 1 Child', 3),
(6, 'Parent 2 Child 1 Child 1', 4),
(7, 'Parent 2 Child 1 Child 2', 4)
GO
WITH T AS
(
SELECT
N.ID, N.Name, N.ParentID, CAST(N.ID AS varbinary(512)) AS MaterializedPath
FROM
Tree N
WHERE
N.ParentID = 0
UNION ALL
SELECT
N.ID, N.Name, N.ParentID, CAST( T.MaterializedPath + CAST(N.ID AS binary(4)) AS varbinary(512) ) AS MaterializedPath
FROM
Tree N INNER JOIN T
ON N.ParentID = T.ID
)
SELECT *
FROM T
ORDER BY T.MaterializedPath
Resultat:
+----+--------------------------+----------+----------------------------+
| ID | Name | ParentID | MaterializedPath |
+----+--------------------------+----------+----------------------------+
| 1 | Parent 1 | 0 | 0x00000001 |
| 3 | Parent 1 Child 1 | 1 | 0x0000000100000003 |
| 5 | Parent 1 Child 1 Child | 3 | 0x000000010000000300000005 |
| 2 | Parent 2 | 0 | 0x00000002 |
| 4 | Parent 2 Child 1 | 2 | 0x0000000200000004 |
| 6 | Parent 2 Child 1 Child 1 | 4 | 0x000000020000000400000006 |
| 7 | Parent 2 Child 1 Child 2 | 4 | 0x000000020000000400000007 |
+----+--------------------------+----------+----------------------------+
Ovanstående rekursiva CTE börjar med rotnoderna. Att beräkna den materialiserade vägen för en rotnod är trivialt okomplicerat, det är själva nodens ID. Vid nästa iteration sammanfogar CTE:n rotnoder med sina undernoder. Den materialiserade sökvägen för en underordnad nod CN
är sammanlänkningen av den materialiserade sökvägen för dess överordnade nod PN
och id för noden CN
. Efterföljande iterationer avancerar en nivå ner på trädet tills lövnoderna nås.