Étant donné un tableau hiérarchique comme celui-ci:
CREATE TABLE [dbo].[btree]
(
id INT PRIMARY KEY
, parent_id INT REFERENCES [dbo].[btree] ([id])
, name NVARCHAR(20)
);
Je souhaite obtenir la structure arborescente complète.
Par exemple, en utilisant ces données:
INSERT INTO [btree] VALUES (1, null, '1 Root');
INSERT INTO [btree] VALUES (2, 1, '1.1 Group');
INSERT INTO [btree] VALUES (3, 1, '1.2 Group');
INSERT INTO [btree] VALUES (4, 2, '1.1.1 Group');
INSERT INTO [btree] VALUES (5, 2, '1.1.2 Group');
INSERT INTO [btree] VALUES (6, 3, '1.2.1 Group');
INSERT INTO [btree] VALUES (7, 3, '1.2.2 Group');
INSERT INTO [btree] VALUES (8, 4, '1.1.1.1 Items');
INSERT INTO [btree] VALUES (9, 4, '1.1.1.2 Items');
INSERT INTO [btree] VALUES (10, 5, '1.1.2.1 Items');
INSERT INTO [btree] VALUES (11, 5, '1.1.1.2 Items');
INSERT INTO [btree] VALUES (12, 6, '1.2.1.1 Items');
INSERT INTO [btree] VALUES (13, 6, '1.2.1.2 Items');
INSERT INTO [btree] VALUES (14, 7, '1.2.2.1 Items');
Je souhaite obtenir:
+----+-----------+---------------------+
| id | parent_id | description |
+----+-----------+---------------------+
| 1 | NULL | 1 Root |
| 2 | 1 | 1.1 Group |
| 4 | 2 | 1.1.1 Group |
| 8 | 4 | 1.1.1.1 Items |
| 9 | 4 | 1.1.1.2 Items |
| 5 | 2 | 1.1.2 Group |
| 10 | 5 | 1.1.2.1 Items |
| 11 | 5 | 1.1.2.2 Items |
| 3 | 1 | 1.2 Group |
| 6 | 3 | 1.2.1 Group |
| 12 | 6 | 1.2.1.1 Items |
| 13 | 6 | 1.2.1.2 Items |
| 7 | 3 | 1.2.2 Group |
| 14 | 7 | 1.2.2.1 Items |
+----+-----------+---------------------+
Je récupère des enregistrements à l'aide d'une requête récursive comme celle-ci:
;WITH tree AS
(
SELECT c1.id, c1.parent_id, c1.name, [level] = 1
FROM dbo.[btree] c1
WHERE c1.parent_id IS NULL
UNION ALL
SELECT c2.id, c2.parent_id, c2.name, [level] = tree.[level] + 1
FROM dbo.[btree] c2 INNER JOIN tree ON tree.id = c2.parent_id
)
SELECT tree.level, tree.id, parent_id, REPLICATE(' ', tree.level - 1) + tree.name AS description
FROM tree
OPTION (MAXRECURSION 0)
;
Et voici le résultat actuel:
+----+-----------+---------------------+
| id | parent_id | description |
| 1 | NULL | 1 Root |
| 2 | 1 | 1.1 Group |
| 3 | 1 | 1.2 Group |
| 6 | 3 | 1.2.1 Group |
| 7 | 3 | 1.2.2 Group |
| 14 | 7 | 1.2.2.1 Items |
| 12 | 6 | 1.2.1.1 Items |
| 13 | 6 | 1.2.1.2 Items |
| 4 | 2 | 1.1.1 Group |
| 5 | 2 | 1.1.2 Group |
| 10 | 5 | 1.1.2.1 Items |
| 11 | 5 | 1.1.1.2 Items |
| 8 | 4 | 1.1.1.1 Items |
| 9 | 4 | 1.1.1.2 Items |
+----+-----------+---------------------+
Je ne sais pas comment le classer par niveaux.
Existe-t-il un moyen de définir un classement pour chaque sous-niveau?
J'ai mis en place un Rextester
la source
c2.id
remplacée par un numéro de ligne et complétée à gauche de sorte que toutes les parties aient la même longueur. Sinon, cela ne fonctionnera pas pour toutes les données. Remplacez simplement 2 par 55 dans les données et la commande changePath
avec une petite correction, pour ajouter du rembourrage.Tricher, juste un peu;) Regardez ma, pas de récursivité!
Testé sur rextester.com
Bien sûr, ce qui précède est plutôt limité. Cela ne fonctionne que sous les hypothèses:
name
colonne a stocké (dans la première partie) le "chemin" réel.CAST .. AS int
n'est nécessaire que si les pièces sont des nombres.Explication: Le code fonctionne en utilisant la fonction
PARSENAME()
qui a pour principal objectif de diviser un nom d'objet en ses 4 parties:Notez que l'ordre est inversé. Par exemple,
PARSENAME('dbo.btree', 2)
nous donnera'dbo'
comme résultat.Avec 3, nous obtiendrons NULL (c'est pourquoi leREVERSE()
est utilisé deux fois dans le code. Sinon, nous obtiendrions les null au début. Le'1.2'
serait analysénull, null, 1, 2
pendant que nous le voulons1, 2, null, null
. )Conclusion: après tout cela, je dois ajouter que la réponse de Bob Campbel est la voie à suivre car elle est plus générale et produit (dans la colonne "chemin" du résultat) la hiérarchie des chemins, qui peut ensuite être utilisée pour le
ORDER BY
.D'autres options que vous pouvez envisager - si la taille de la table augmente et que la solution récursive devient lente - consiste à stocker le chemin dans une colonne distincte (dans un format qui est bon pour la commande, c'est-à-dire avec un remplissage) ou à utiliser le fourni
HierarchyID
type qui correspond exactement à ce cas d'utilisation, les données hiérarchiques.la source
name
ne peut pas être utilisé dans ce cas. Il me faudra toute la nuit pour le déchiffrer, pourrais-je avoir une explication?name
stocke un chemin (avec du texte), comme'order173.palletA27.box9'.bag3A
, vous pouvez toujours utiliser le code (il suffit de supprimer les transtypages en int). Dans tous les cas, la requête de BenCambell est la voie à suivre en général.