La solution que je propose ici utilise le concept de chemin matérialisé. Voici un exemple de chemins matérialisés utilisant vos exemples de données. J'espère que cela vous aidera à comprendre le concept de chemin matérialisé :
+----+--------------------------+----------+------------------+
| 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 |
+----+--------------------------+----------+------------------+
Chaque nœud N
a un chemin matérialisé, ce chemin vous indique le chemin pour aller du noeud racine au noeud N
. Il peut être construit en concaténant les identifiants de nœud. Par exemple, pour atteindre le nœud 5
à partir de son nœud racine, vous visitez le nœud 1
, nœud 3
, et le nœud 5
, donc nœud 5
le chemin matérialisé est 1.3.5
Par coïncidence, la commande que vous recherchez peut être obtenue en commandant par le chemin matérialisé.
Dans l'exemple précédent, les chemins matérialisés consistent à concaténer des chaînes, mais je préfère la concaténation binaire pour un certain nombre de raisons.
Pour construire les chemins matérialisés, vous avez besoin du CTE récursif suivant :
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
Résultat :
+----+--------------------------+----------+----------------------------+
| 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 |
+----+--------------------------+----------+----------------------------+
Le CTE récursif ci-dessus commence par les nœuds racine. Le calcul du chemin matérialisé pour un nœud racine est très simple, c'est l'ID du nœud lui-même. À l'itération suivante, le CTE joint les nœuds racine avec ses nœuds enfants. Le chemin matérialisé pour un nœud enfant CN
est la concaténation du chemin matérialisé de son nœud parent PN
et l'id du nœud CN
. Les itérations suivantes avancent d'un niveau vers le bas dans l'arbre jusqu'à ce que les nœuds feuilles soient atteints.