2009-09-04 11 views
4

J'ai la table hiérarchique suivante:Quelle est la manière la plus rapide/la plus simple de dénormaliser cette table hiérarchique en une table plate?

Table Category: 
CategoryId, ParentCategoryId, CategoryName 
1, null, SomeRoot 
2, 1, SomeChild 
3, 2, SomeGrandchild 
4, 3, SomeGreatGrandchild 

(notez ces données d'échantillon ne comprend pas une feuille sur un nœud plus haut que le niveau 4, mais cela est possible). Les données ne seront jamais plus profondes que le niveau 4, si cela est pertinent. Je voudrais transformer/pivot à ce fixe affichage 4 niveaux

CatId, Name1, Name2, Name3, Name4 
1, SomeRoot, null, null, null 
2, SomeRoot, SomeChild, null, null 
3, SomeRoot, SomeChild, SomeGrandchild, null 
4, SomeRoot, SomeChild, SomeGrandchild, SomeGreatGrandchild 

Je l'ai fait parti des jointures externes à la table de catégorie 4 fois, et construit une déclaration énorme de cas pour détecter le niveau à utiliser pour la Champ ID, mais cela n'inclut pas les lignes nulles .... Des idées? AIDEZ-MOI!

+1

Vous devez nous indiquer si les données auront toujours jcollum

+1

@jcollum Mise à jour Q: Les données ne seront jamais plus profondes que le niveau 4, si cela est pertinent. – TheSoftwareJedi

+1

S'il a des niveaux de récursion inconnus, vous voulez utiliser un CTE. Pour les niveaux de récursion connus et petits, j'entends qu'un CTE a trop de frais généraux, donc une sous-requête est meilleure. – jcollum

Répondre

0

Essayez ceci:

Select C.CatId, C.Name, PC.Name, GP.Name, GGP.Name 
    From Category C 
    Left Join Category PC On PC.CatId = C.ParentCategoryId 
    Left Join Category GP On GP .CatId = PC.ParentCategoryId 
    Left Join Category GGP On GGP .CatId = GP.ParentCategoryId 

D'après année commentaires, si vous écrivez une UDF comme suit:

Create Function CatParentNames 
(@CatId Integer) 
Returns varchar(1000) 
AS 
Begin 
    Declare @outVal VarChar(1000) 
    Declare @ParId Integer 
    Select @ParId = ParentCategoryId, @outVal = Name 
    From Category 
    Where CatId = @CatId 
    While Exists(Select * From Category 
        Where CatId = @ParId) 
     Begin 
      Select @ParId = ParentCategoryId, 
       @outVal = Name + ', ' + @outVal 
      From Category 
      Where CatId = @ParId 
     End 

Return @outVal 

End 

alors, écrivez votre sql comme suit:

Select CatId, dbo.CatParentNames(CatId) 
From Category 
Where ParentCategoryId Is Not Null 
+0

Je l'avais à l'origine - mais avec un "où C.parentcategoryid est nul" pour le garder à la racine. Cependant, dans cet exemple ci-dessus, seul le résultat 1 sera retourné. En outre, l'Id serait le – TheSoftwareJedi

+0

de la racine l'essayer sans la clause where? Cause that where clause DEVRAIT la restreindre au seul enregistrement racine (c'est le seul avec parentCatId nul) –

+0

La façon dont je l'ai écrit devrait retourner toutes les lignes de la table Category, car il n'y a pas de clause where, et les jointures sont toutes les jointures externes .... –

0

Essayez ceci:

select a.CategoryId as CatId, 
a.CategoryName as Name1, 
cast(null as varchar(20)) as Name2, 
cast(null as varchar(20)) as Name3, 
cast(null as varchar(20)) as Name4 
from @YourTable a 
where a.ParentCategoryId is null 

union all 

select b.CategoryId, 
a.CategoryName, 
b.CategoryName, 
null, 
null 
from @YourTable a 
inner join @YourTable b 
on a.CategoryId = b.ParentCategoryId 
where a.ParentCategoryId is null 


union all 

select c.CategoryId, 
a.CategoryName, 
b.CategoryName, 
c.CategoryName, 
null 
from @YourTable a 
inner join @YourTable b 
on a.CategoryId = b.ParentCategoryId 
inner join @YourTable c 
on b.CategoryId = c.ParentCategoryId 
where a.ParentCategoryId is null 

union all 

select d.CategoryId, 
a.CategoryName, 
b.CategoryName, 
c.CategoryName, 
d.CategoryName 
from @YourTable a 
inner join @YourTable b 
on a.CategoryId = b.ParentCategoryId 
inner join @YourTable c 
on b.CategoryId = c.ParentCategoryId 
inner join @YourTable d 
on c.CategoryId = d.ParentCategoryId 
order by 2, 3, 4, 5 

Mais ce n'est pas la façon de le construire si vous allez l'utiliser avec un TreeView à plusieurs colonnes qui ressemble à ceci:

alt text http://www.digitaltools.com/images/GVT.jpg

Plus peut être trouvé here.

+0

vous devez ajouter une colonne supplémentaire à la première requête, donc tous ont le même nombre de colonnes, et un tri aiderait –

+0

Ok, corrigé et ajouté. – JBrooks

+0

lorsque je l'exécute en utilisant ma table de test et mes données je n'ai que des lignes pour le 1er et le dernier de chaque "set" –

3

ce qui a probablement est pas la requête la plus efficace, mais il est le plus facile à code:

declare @YourTable table (CategoryId int primary key, ParentCategoryId int , CategoryName varchar(50)) 

INSERT INTO @YourTable VALUES (1, null, 'SomeRoot') 
INSERT INTO @YourTable VALUES (2, 1, 'SomeChild') 
INSERT INTO @YourTable VALUES (3, 2, 'SomeGrandchild') 
INSERT INTO @YourTable VALUES (4, 3, 'SomeGreatGrandchild') 

INSERT INTO @YourTable VALUES (10, null, 'X_SomeRoot') 
INSERT INTO @YourTable VALUES (20, 10, 'X_SomeChild') 
INSERT INTO @YourTable VALUES (30, 20, 'X_SomeGrandchild') 


Select 
    c1.CategoryId, c1.CategoryName, c2.CategoryName, c3.CategoryName, c4.CategoryName 
    From @YourTable   c1 
     INNER JOIN @YourTable c2 On c1.CategoryId = c2.ParentCategoryId 
     INNER JOIN @YourTable c3 On c2.CategoryId = c3.ParentCategoryId 
     INNER JOIN @YourTable c4 On c3.CategoryId = c4.ParentCategoryId 
    WHERE c1.ParentCategoryId IS NULL 
UNION 
Select 
    c1.CategoryId, c1.CategoryName, c2.CategoryName, c3.CategoryName, NULL 
    From @YourTable   c1 
     INNER JOIN @YourTable c2 On c1.CategoryId = c2.ParentCategoryId 
     INNER JOIN @YourTable c3 On c2.CategoryId = c3.ParentCategoryId 
    WHERE c1.ParentCategoryId IS NULL 
UNION 
Select 
    c1.CategoryId, c1.CategoryName, c2.CategoryName, NULL, NULL 
    From @YourTable   c1 
     INNER JOIN @YourTable c2 On c1.CategoryId = c2.ParentCategoryId 
    WHERE c1.ParentCategoryId IS NULL 
UNION 
Select 
    c1.CategoryId, c1.CategoryName, NULL, NULL, NULL 
    From @YourTable   c1 
    WHERE c1.ParentCategoryId IS NULL 
ORDER BY 2,3,4,5 

SORTIE:

SortB CategoryId CategoryName CategoryName CategoryName  CategoryName 
----- ----------- ------------ ------------- ----------------- -------------------- 
1  1   SomeRoot  NULL   NULL    NULL 
2  1   SomeRoot  SomeChild  NULL    NULL 
3  1   SomeRoot  SomeChild  SomeGrandchild NULL 
4  1   SomeRoot  SomeChild  SomeGrandchild SomeGreatGrandchild 
1  10   X_SomeRoot NULL   NULL    NULL 
2  10   X_SomeRoot X_SomeChild NULL    NULL 
3  10   X_SomeRoot X_SomeChild X_SomeGrandchild NULL 

(7 row(s) affected) 
+0

Merci d'avoir ajouté l'insertion @table, +1 – jcollum