Sql server 将此Hierarchical表反规范化为平面表的最快/最简单的方法是什么?

Sql server 将此Hierarchical表反规范化为平面表的最快/最简单的方法是什么?,sql-server,sql-server-2005,hierarchical-data,Sql Server,Sql Server 2005,Hierarchical Data,我有以下分层表: Table Category: CategoryId, ParentCategoryId, CategoryName 1, null, SomeRoot 2, 1, SomeChild 3, 2, SomeGrandchild 4, 3, SomeGreatGrandchild (注意,此示例数据不包括级别4之前的节点上的叶,但这是可能的)。如果相关的话,数据永远不会超过4级。我想将其转换/旋转到这个固定的4级显示器 CatId, Name1, Name2, Name3, N

我有以下分层表:

Table Category:
CategoryId, ParentCategoryId, CategoryName
1, null, SomeRoot
2, 1, SomeChild
3, 2, SomeGrandchild
4, 3, SomeGreatGrandchild
(注意,此示例数据不包括级别4之前的节点上的叶,但这是可能的)。如果相关的话,数据永远不会超过4级。我想将其转换/旋转到这个固定的4级显示器

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
我已经做了4次与category表的left-outer连接,并构建了一个庞大的case语句来检测ID字段使用的级别,但这不包括空行。。。。有什么想法吗?救命啊

试试这个:

  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
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
根据您的评论,如果您按照以下方式编写自定义项:

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
Select CatId, dbo.CatParentNames(CatId)
From Category
Where ParentCategoryId Is Not Null
然后,按如下方式编写sql:

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
Select CatId, dbo.CatParentNames(CatId)
From Category
Where ParentCategoryId Is Not Null
试试这个:

  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
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
但是,如果您要将它与如下所示的多列树视图一起使用,则这不是构建它的方法:


(来源:)


可以找到更多信息

这可能不是最有效的查询,但它是最容易编码的:

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
输出:

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)

你需要告诉我们数据是否总是更新@jcollum问:如果相关的话,数据永远不会超过级别4。如果它有未知的递归级别,你想使用CTE。对于已知的和小级别的递归,我听说CTE有太多的开销,所以子查询更好。这里不需要CTE,因为它总是只有4。但任何有效的都是最好的!这里的转换不需要很大的性能。我正在对数据进行非规范化处理,以便在前端进行高效读取。如果构建需要一分钟,我不在乎。我本来就有这个功能,但是用了一个“where C.parentcategoryid is null”来保持它离开根目录。但是,在上面的示例中,它只返回1个结果。而且,Id将是根,如果没有where子句,它将是根吗?因为where子句会将其限制为只有一个根记录(这是唯一一个parentCatId为null的记录),就像我编写它的方式一样,它应该返回Category表中的每一行,因为没有where子句,连接都是外部连接……它不会输出我问题中写入的数据。对于ID2,它将输出2,SomeChild,null,null,null。对于Id 3,它将输出3,some孙子,null,null,null。这两个都是错误的。所以您只希望每个子记录的输出中有一条记录,而不是根记录?在示例输出中,每一行都有someroot作为name1的值,这使我怀疑您需要CatId,然后是该对象中所有对象的名称。正确吗?您需要在第一个查询中添加一个额外的列,这样所有查询都具有相同的列数,并且排序会有所帮助。当我使用测试表和数据运行它时,我只获取每个“集合”的第一行和最后一行。您是对的,它现在是固定的,现在使用另一篇文章中的@YourTable表。感谢您在@table insert中添加,+1