web-dev-qa-db-fra.com

Récurrence CTE pour obtenir la hiérarchie

Je dois obtenir une hiérarchie ordonnée d'un arbre, d'une manière spécifique. La table en question ressemble un peu à ceci (tous les champs d'ID sont des identifiants uniques, j'ai simplifié les données à titre d'exemple):

EstimateItemID EstimateID ParentEstimateItemID ItemType 
 -------------- ---------- ----------------- --- -------- 
 1 Un produit NULL 
 2 Un 1 produit 
 3 Un service 2 
 4 Un produit NULL 
 5 A 4 produit 
 6 A 5 service 
 7 A 1 service 
 8 A 4 produit

Vue graphique de l'arborescence (* indique 'service'):

 A 
 ___/\ ___ 
/\ 
 1 4 
/\/\ 
 2 7 * 5 8 
//
3* 6 * 

En utilisant cette requête, je peux obtenir la hiérarchie (supposons simplement que "A" est un identifiant unique, je sais que ce n’est pas dans la vie réelle):

DECLARE @EstimateID uniqueidentifier
SELECT @EstimateID = 'A'

;WITH temp as(
    SELECT * FROM EstimateItem
    WHERE EstimateID = @EstimateID

    UNION ALL

    SELECT ei.* FROM EstimateItem ei
    INNER JOIN temp x ON ei.ParentEstimateItemID = x.EstimateItemID
)

SELECT * FROM temp

Cela me donne les enfants de EstimateID 'A', mais dans l'ordre dans lequel il apparaît dans le tableau. c'est à dire:

EstimateItemID 
 -------------- 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8

Malheureusement, il me faut une hiérarchie ordonnée avec un ensemble de résultats qui respecte les contraintes suivantes:

1. chaque branche doit être groupée 
 2. les enregistrements avec ItemType 'product' et parent sont le nœud supérieur 
 3. enregistrements avec ItemType 'product' et parent non NULL groupés après le nœud supérieur 
 4. les enregistrements avec ItemType 'service' sont le noeud inférieur d'une branche 

Ainsi, l'ordre dans lequel j'ai besoin des résultats, dans cet exemple, est:

EstimateItemID 
 -------------- 
 1 
 2 
 3 
 7 
 4 
 5 
 8 
 6 

Que dois-je ajouter à ma requête pour y parvenir?

45
Woods8460

Essaye ça:

;WITH items AS (
    SELECT EstimateItemID, ItemType
    , 0 AS Level
    , CAST(EstimateItemID AS VARCHAR(255)) AS Path
    FROM EstimateItem 
    WHERE ParentEstimateItemID IS NULL AND EstimateID = @EstimateID

    UNION ALL

    SELECT i.EstimateItemID, i.ItemType
    , Level + 1
    , CAST(Path + '.' + CAST(i.EstimateItemID AS VARCHAR(255)) AS VARCHAR(255))
    FROM EstimateItem i
    INNER JOIN items itms ON itms.EstimateItemID = i.ParentEstimateItemID
)

SELECT * FROM items ORDER BY Path

Avec Path - lignes a triées par noeuds parents

Si vous voulez trier les enfants de ItemType pour chaque niveau, vous pouvez jouer avec Level et SUBSTRING de Pathcolumn ....

Ici SQLFiddle avec un échantillon de données

77
Fabio

C'est un ajout à la grande idée de Fabio vue d'en haut. Comme je l'ai dit dans ma réponse à son message d'origine. J'ai republié son idée en utilisant des données, un nom de table et des champs plus communs pour que d'autres puissent le suivre plus facilement.

Merci Fabio! Grand nom en passant.

D'abord quelques données avec lesquelles travailler:

CREATE TABLE tblLocations (ID INT IDENTITY(1,1), Code VARCHAR(1), ParentID INT, Name VARCHAR(20));

INSERT INTO tblLocations (Code, ParentID, Name) VALUES
('A', NULL, 'West'),
('A', 1, 'WA'),
('A', 2, 'Seattle'),
('A', NULL, 'East'),
('A', 4, 'NY'),
('A', 5, 'New York'),
('A', 1, 'NV'),
('A', 7, 'Las Vegas'),
('A', 2, 'Vancouver'),
('A', 4, 'FL'),
('A', 5, 'Buffalo'),
('A', 1, 'CA'),
('A', 10, 'Miami'),
('A', 12, 'Los Angeles'),
('A', 7, 'Reno'),
('A', 12, 'San Francisco'),
('A', 10, 'Orlando'),
('A', 12, 'Sacramento');

Maintenant la requête récursive:

-- Note: The 'Code' field isn't used, but you could add it to display more info.
;WITH MyCTE AS (
  SELECT ID, Name, 0 AS TreeLevel, CAST(ID AS VARCHAR(255)) AS TreePath
  FROM tblLocations T1
  WHERE ParentID IS NULL

  UNION ALL

  SELECT T2.ID, T2.Name, TreeLevel + 1, CAST(TreePath + '.' + CAST(T2.ID AS VARCHAR(255)) AS VARCHAR(255)) AS TreePath
  FROM tblLocations T2
  INNER JOIN MyCTE itms ON itms.ID = T2.ParentID
)
-- Note: The 'replicate' function is not needed. Added it to give a visual of the results.
SELECT ID, Replicate('.', TreeLevel * 4)+Name 'Name', TreeLevel, TreePath
FROM  MyCTE 
ORDER BY TreePath;
14
ptownbro

Je crois que vous devez ajouter ce qui suit aux résultats de votre CTE ...

  1. BranchID = une sorte d'identifiant qui identifie de manière unique la branche. Pardonnez-moi de ne pas être plus spécifique, mais je ne suis pas sûr de ce qui identifie une branche pour vos besoins. Votre exemple montre un arbre binaire dans lequel toutes les branches retournent à la racine.
  2. ItemTypeID où (par exemple) 0 = Produit et 1 = Service.
  3. Parent = identifie le parent.

Si ceux-ci existent dans la sortie, je pense que vous devriez pouvoir utiliser la sortie de votre requête comme un autre CTE ou comme clause FROM dans une requête. Ordre par BranchID, ItemTypeID, Parent.

0
DeadZone