ORDRE hiérarchique de l'arbre parent-enfant


21

Je dois suivre les données dans SQL Server 2008 R2. SQLFiddle

Schéma:

CREATE TABLE [dbo]. [ICFilters] (
   [ICFilterID] [int] IDENTITY (1,1) NOT NULL,
   [ParentID] [int] NOT NULL DEFAULT 0,
   [FilterDesc] [varchar] (50) NOT NULL,
   [Active] [tinyint] NOT NULL DEFAULT 1,
 CONTRAINTE [PK_ICFilters] CLÉ PRIMAIRE CLUSTERED 
 ([ICFilterID] ASC) AVEC 
    PAD_INDEX = OFF,
    STATISTICS_NORECOMPUTE = OFF,
    IGNORE_DUP_KEY = OFF,
    ALLOW_ROW_LOCKS = ON,
    ALLOW_PAGE_LOCKS = ON
 ) LE [PRIMAIRE]
) LE [PRIMAIRE]

INSERT INTO [dbo]. [ICFilters] (ParentID, FilterDesc, Active)
Valeurs 
(0, «Type de produit», 1),
(1, «ProdSubType_1», 1),
(1, «ProdSubType_2», 1),
(1, «ProdSubType_3», 1),
(1, «ProdSubType_4», 1),
(2, 'PST_1.1', 1),
(2, 'PST_1.2', 1),
(2, «PST_1.3», 1),
(2, «PST_1.4», 1),
(2, «PST_1.5», 1),
(2, «PST_1.6», 1),
(2, 'PST_1.7', 0),
(3, «PST_2.1», 1),
(3, 'PST_2.2', 0),
(3, «PST_2.3», 1),
(3, «PST_2.4», 1),
(14, «PST_2.2.1», 1),
(14, «PST_2.2.2», 1),
(14, «PST_2.2.3», 1),
(3, 'PST_2.8', 1)

Table:

| ICFILTERID | PARENTID | FILTERDESC | ACTIF |
--------------------------------------------------
| 1 | 0 | Type de produit | 1 |
| 2 | 1 | ProdSubType_1 | 1 |
| 3 | 1 | ProdSubType_2 | 1 |
| 4 | 1 | ProdSubType_3 | 1 |
| 5 | 1 | ProdSubType_4 | 1 |
| 6 | 2 | PST_1.1 | 1 |
| 7 | 2 | PST_1.2 | 1 |
| 8 | 2 | PST_1.3 | 1 |
| 9 | 2 | PST_1.4 | 1 |
| 10 | 2 | PST_1.5 | 1 |
| 11 | 2 | PST_1.6 | 1 |
| 12 | 2 | PST_1.7 | 0 |
| 13 | 3 | PST_2.1 | 1 |
| 14 | 3 | PST_2.2 | 0 |
| 15 | 3 | PST_2.3 | 1 |
| 16 | 3 | PST_2.4 | 1 |
| 17 | 14 | PST_2.2.1 | 1 |
| 18 | 14 | PST_2.2.2 | 1 |
| 19 | 14 | PST_2.2.3 | 1 |
| 20 | 3 | PST_2.8 | 1 |

Chaque ligne a l'ID de son parent et celle de la racine parentid = 0. Les FilterDescs ne sont que des exemples de descriptions, donc je ne peux pas essayer de les analyser pour les commander.

La question

Est-il possible de sélectionner toutes les lignes de manière arborescente? Si c'est le cas, comment? Quand je dis «en forme d'arbre», je veux dire sélectionner récursivement le parent suivi de tous ses enfants, puis tous les enfants de chacun d'entre eux et ainsi de suite. Une première traversée d'arbre en profondeur.

Mes amis et moi avons essayé mais nous n'avons pas réussi à trouver des solutions de travail, mais nous continuerons d'essayer. Je suis assez nouveau dans sql, alors peut-être que cela peut être fait facilement et je rends les choses plus difficiles que nécessaire.

Exemple de sortie (souhaitée):

| ICFILTERID | PARENTID | FILTERDESC | ACTIF |
--------------------------------------------------
| 1 | 0 | Type de produit | 1 |
| 2 | 1 | ProdSubType_1 | 1 |
| 6 | 2 | PST_1.1 | 1 |
| 7 | 2 | PST_1.2 | 1 |
| 8 | 2 | PST_1.3 | 1 |
| 9 | 2 | PST_1.4 | 1 |
| 10 | 2 | PST_1.5 | 1 |
| 11 | 2 | PST_1.6 | 1 |
| 12 | 2 | PST_1.7 | 0 |
| 3 | 1 | ProdSubType_2 | 1 |
| 13 | 3 | PST_2.1 | 1 |
| 14 | 3 | PST_2.2 | 0 |
| 17 | 14 | PST_2.2.1 | 1 |
| 18 | 14 | PST_2.2.2 | 1 |
| 19 | 14 | PST_2.2.3 | 1 |
| 15 | 3 | PST_2.3 | 1 |
| 16 | 3 | PST_2.4 | 1 |
| 20 | 3 | PST_2.8 | 1 |
| 4 | 1 | ProdSubType_3 | 1 |
| 5 | 1 | ProdSubType_4 | 1 |

Il serait préférable d'utiliser CTE
Kin Shah

1
Voici un fil de discussion montrant le tri des résultats souhaité sans que les données de la table ne soient chargées dans un ordre spécifique. Il utilise row_number () et partitionne par pour créer un "chemin" qui permet le tri souhaité. ask.sqlservercentral.com/questions/48518/…

Réponses:


25

OK, assez de cellules cérébrales sont mortes.

SQL Fiddle

WITH cte AS
(
  SELECT 
    [ICFilterID], 
    [ParentID],
    [FilterDesc],
    [Active],
    CAST(0 AS varbinary(max)) AS Level
  FROM [dbo].[ICFilters]
  WHERE [ParentID] = 0
  UNION ALL
  SELECT 
    i.[ICFilterID], 
    i.[ParentID],
    i.[FilterDesc],
    i.[Active],  
    Level + CAST(i.[ICFilterID] AS varbinary(max)) AS Level
  FROM [dbo].[ICFilters] i
  INNER JOIN cte c
    ON c.[ICFilterID] = i.[ParentID]
)

SELECT 
  [ICFilterID], 
  [ParentID],
  [FilterDesc],
  [Active]
FROM cte
ORDER BY [Level];

2
C'est exactement ce dont j'avais besoin! Je suis d'accord trop de cellules cérébrales sont mortes à ce sujet. N'étais-je pas clair dans ce que je voulais? Si c'est le cas, je vais modifier la question pour référence future. Je rendais les choses plus difficiles que nécessaire ...
Archangel33

1
@ Archangel33 Vous avez bien expliqué le problème et ce dont vous avez besoin. De plus, sqlfiddle aide vraiment.
Travis

2
+1 mais en utilisant [ICFilterID] [int] IDENTITY (1,1) pour le tri ne fonctionnera que si les éléments sont insérés dans le bon ordre, mais un autre champ pour le tri n'est pas encore implémenté par OT
bummi

4
Je ne pense pas que ce soit une solution 100% correcte. Bien qu'il répertorie toutes les lignes avec leur niveau correct dans la hiérarchie, il ne les répertorie pas dans l'ordre dans lequel la question leur a été posée. Serait-il possible de lister les lignes dans le bon ordre selon la question? C'est ce que je recherche aussi.

1
Cela répond à ma question car les données fournies dans la [FilterDesc]colonne sont fictives et cet ordre est inutile / sans importance. Suivant la logique de la réponse de @Travis Gan, tout ce que l'on doit faire pour obtenir ce classement est d'en ajouter un autre CASTau Level. par exemple. Level + CAST( CAST(i.[ICFilterID] AS varbinary(max)) AS LevelDevient Level + CAST(i.[FilterDesc] AS varbinary(max)) + CAST(i.[ICFilterID] AS varbinary(max)) AS Level.
Archangel33

1

Ce qui précède ne semble pas fonctionner correctement pour moi. Imaginez une configuration à 2 tables avec des données de type facebook. Le tableau 1, a PostId + vous d'autres champs. PostId est incrémenté automatiquement et, évidemment, dans votre interface, vous trierez DESC pour avoir le dernier message en haut.

Maintenant, pour le tableau des commentaires. Tableau 2 Ce tableau CommentId est la clé primaire, numéro automatique. Dans votre interface graphique, vous souhaitez l'afficher en ASC, afin que lors de la lecture du fil, cela ait du sens. (le plus ancien (nombre le plus petit) en haut) Les autres clés importantes du tableau 2 sont: PostId (FK retour aux publications) et ParentId (FK à CommentId) où ParentId sera NULL s'il s'agit du commentaire "racine" sur une publication. Si quelqu'un RÉPOND à un commentaire, le parentId sera rempli avec le commentid.
J'espère que vous obtenez la dérive. Le CTE ressemblera à ceci:

WITH  Comments
        AS ( SELECT  CommentId , ParentId, CAST(CommentId AS VARBINARY(MAX)) AS Sortkey, 0 AS Indent
             FROM    dbo.Comments
             WHERE   ParentId IS NULL AND PostId = 105
             UNION ALL
             SELECT  b.CommentId , b.ParentId,  c.Sortkey + CAST(b.CommentId AS varbinary(max))  AS Sortkey, c.Indent + 1 AS Indent
             FROM    dbo.Comments b
             INNER JOIN Comments c ON c.CommentId = b.ParentId
           )
   SELECT   *
   FROM     Comments
   ORDER BY Sortkey

Exemple de sortie

1   NULL    0x0000000000000001  0
5   1   0x00000000000000010000000000000001  1
6   5   0x000000000000000100000000000000010000000000000005  2
2   NULL    0x0000000000000002  0

Sur F / B post 105, il y avait deux commentaires (CommentIds 1 et 2) Quelqu'un a ensuite répondu sur Comment1 (CommentId 5, ParentId 1), puis quelqu'un d'autre a commenté cette réponse, ainsi de suite Comment5 (CommentId 6, ParentId 6)

Et l'alto, la séquence est correcte, sous le post, vous pouvez maintenant afficher les commentaires dans la bonne séquence. Pour mettre en retrait les messages afin qu'ils se forment et se dessinent comme dans Facebook (plus le niveau est profond, plus il doit être margé à partir de la gauche), j'ai également une colonne appelée Retrait. Les racines sont 0, puis dans l'union, nous avons c.Indent + 1 AS Indent Dans le code, vous pouvez maintenant multiplier le retrait avec supposons 32px et afficher les commentaires dans une hiérarchie et un contour agréables.

Je ne vois aucun problème à utiliser la clé primaire d'incrémentation automatique CommentId comme force motrice pour la création de ma SortKey, car il y a un meilleur changement de vous gâcher les dates (date de commentaire) que de gâcher une clé gérée par la base de données qui contient +1


0
create table pc ( parent varchar(10), child varchar(10) )

insert into pc values('a','b');
insert into pc values('a','c');
insert into pc values('b','e');
insert into pc values('b','f');
insert into pc values('a','d');
Insert into pc values('b','g');
insert into pc values('c','h');
insert into pc values('c','i');
insert into pc values('d','j');
insert into pc values('f','k');
insert into pc values('x','y');
insert into pc values('y','z');
insert into pc values('m','n');

 DECLARE @parent varchar(10) = 'a';
 WITH cte AS
 (
  select null parent, @parent child, 0 as level
   union
  SELECT  a.parent, a.child , 1 as level
    FROM pc a
   WHERE a.parent = @parent
   UNION ALL
  SELECT a.parent, a.child , c.level +    1
  FROM pc a JOIN cte c ON a.parent = c.child
  )
  SELECT distinct parent, child , level
  FROM cte
  order by level, parent

Cela vous donnera tous les descendants et le niveau.
J'espère que cela t'aides :)

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.