Решението, което предлагам тук, използва концепцията за материализиран път. По-долу е даден пример за материализирани пътища, използващи вашите примерни данни. Надявам се, че ще ви помогне да разберете концепцията за материализиран път:
+----+--------------------------+----------+------------------+
| 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 |
+----+--------------------------+----------+------------------+
Всеки възел N
има материализиран път, този път ви казва пътя да преминете от основния възел до възела N
. Може да се изгради като конкатенира идентификаторите на възела. Например, за да достигнете до възел 5
започвайки от неговия основен възел, посещавате възел 1
, възел 3
и възел 5
, така че възел 5
материализираният път е 1.3.5
По съвпадение, поръчката, която търсите, може да бъде постигната чрез материализирания път.
В предишния пример материализираните пътища са изградени от конкатениращи низове, но аз предпочитам двоична конкатенация по редица причини.
За да изградите материализираните пътища, имате нужда от следния рекурсивен 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
Резултат:
+----+--------------------------+----------+----------------------------+
| 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 |
+----+--------------------------+----------+----------------------------+
Горният рекурсивен CTE започва с основните възли. Изчисляването на материализирания път за основен възел е тривиално просто, това е идентификаторът на самия възел. На следващата итерация CTE свързва основните възли със своите дъщерни възли. Материализираният път за дъщерен възел CN
е конкатенацията на материализирания път на неговия родителски възел PN
и идентификатора на възел CN
. Следващите итерации напредват с едно ниво надолу в дървото, докато се достигнат листните възли.