在Parent Child关系中使用hierarchyId

时间:2016-03-15 13:13:32

标签: sql sql-server

我有一个名为 tb_Section 的表格。它旨在支持n级层次结构。每行都有一个 ParentSetionId ,如果是父/子关系,则指向另一行 SectionId

现在,我有一个过程,我必须将这些部分逐个复制到另一个表,保持层次结构不变。我按照以下顺序安排了部分:

Select 
 s.SectionId,
 s.ParentSectionId,
 s.SectionName
From 
 tb_Section s
Where
 s.TemplateId = 123
Order By
 Convert(hierarchyid, '/' + cast(sec.ParentSectionId as varchar)+ '/')

我将这些插入到TempTable中,并将每个部分逐个插入另一个表中。这工作正常。但在申请上线后,报道了一个问题。

用户首先将所有部分创建为父部分,然后逐个编辑它们并使其成为其他部分的子级(使用最困难的方式:/)部分层次结构如下:

Super-Parent
      Parent1
          Child1
          Child2
      Parent2
          Child3
          Child4

在这种情况下,上述查询按顺序返回以下行。

SuperParent
Child1
Child2
Parent1
Child3
Child4
Parent2

而不是正常情况,应该是这个

SuperParent
Parent1    
Child1
Child2
Parent2
Child3
Child4

对此有何帮助?我似乎无法找到发生这种情况的原因。

更新

这是首先插入子项的SQL脚本。

CREATE TABLE tb_Section(
   SectionId BIGINT  NOT NULL PRIMARY KEY 
  ,ParentSectionId BIGINT 
  ,SectionName NVARCHAR(50) NOT NULL
);
INSERT INTO tb_Section(SectionId,ParentSectionId,SectionName) VALUES (SectionId,ParentSectionId,N'SectionName');
INSERT INTO tb_Section(SectionId,ParentSectionId,SectionName) VALUES (686772,686776,N'Child 1');
INSERT INTO tb_Section(SectionId,ParentSectionId,SectionName) VALUES (686773,686776,N'Child 2');
INSERT INTO tb_Section(SectionId,ParentSectionId,SectionName) VALUES (686774,686777,N'Child 3');
INSERT INTO tb_Section(SectionId,ParentSectionId,SectionName) VALUES (686775,686777,N'Child 4');
INSERT INTO tb_Section(SectionId,ParentSectionId,SectionName) VALUES (686776,686778,N'Parent 1');
INSERT INTO tb_Section(SectionId,ParentSectionId,SectionName) VALUES (686777,686778,N'Parent 2');
INSERT INTO tb_Section(SectionId,ParentSectionId,SectionName) VALUES (686778,NULL,N'Super-Parent');

以上是我的上述查询返回 hierarchyId

的结果
+-----------+-----------------+---------------+--------------+----------------+
| SectionId | ParentSectionId | SectionTypeId | SectionName  |   hierarchy    |
+-----------+-----------------+---------------+--------------+----------------+
|    686778 | NULL            |             1 | Super-Parent | NULL           |
|    686772 | 686776          |             2 | Child 1      | 0xF800298C6620 |
|    686773 | 686776          |             2 | Child 2      | 0xF800298C6620 |
|    686774 | 686777          |             2 | Child 3      | 0xF800298C6660 |
|    686775 | 686777          |             2 | Child 4      | 0xF800298C6660 |
|    686776 | 686778          |             2 | Parent 1     | 0xF800298C66A0 |
|    686777 | 686778          |             2 | Parent 2     | 0xF800298C66A0 |
+-----------+-----------------+---------------+--------------+----------------+

您可以看到层次结构不同,此处子项显示在“父项”之前。我希望这些样本数据有所帮助。

2 个答案:

答案 0 :(得分:1)

尝试使用递归CTE并创建自己的层次结构字符串:

;WITH Recurs
AS
(
    SELECT SectionId, ParentSectionId, 
           SectionName, '/' + CAST(SectionId AS VARCHAR(MAX)) as Level
    FROM tb_Section
    WHERE ParentSectionId Is NULL
    UNION ALL
    SELECT s.SectionId, s.ParentSectionId, s.SectionName, 
           CAST(r.level + '/' + CAST(s.SectionId AS VARCHAR(MAX) )AS Varchar(MAX))
    FROM tb_Section s
    INNER JOIN recurs r
        ON S.ParentSectionId = r.SectionId
)
SELECT *
FROM Recurs
ORDER BY Level

产地:

SectionId   ParentSectionId SectionName  Level
686778      NULL            Super-Parent /686778
686776      686778          Parent 1     /686778/686776
686772      686776          Child 1      /686778/686776/686772
686773      686776          Child 2      /686778/686776/686773
686777      686778          Parent 2     /686778/686777
686774      686777          Child 3      /686778/686777/686774
686775      686777          Child 4      /686778/686777/686775

答案 1 :(得分:1)

根据您提供的数据,HierarchyID值对我来说不正确。当我运行此查询时:

CREATE TABLE tb_Section(
   SectionId BIGINT  NOT NULL PRIMARY KEY 
  ,ParentSectionId BIGINT 
  ,SectionName NVARCHAR(50) NOT NULL
  , h HIERARCHYID
);

INSERT  INTO [tb_Section]
        ( [SectionId], [ParentSectionId], [SectionName], [h] )
VALUES  ( 686778, NULL, 'Super-Parent', NULL ),
        ( 686772, 686776, 'Child 1', 0xF800298C6620 ),
        ( 686773, 686776, 'Child 2', 0xF800298C6620 ),
        ( 686774, 686777, 'Child 3', 0xF800298C6660 ),
        ( 686775, 686777, 'Child 4 ', 0xF800298C6660 ),
        ( 686776, 686778, 'Parent 1 ', 0xF800298C66A0 ),
        ( 686777, 686778, 'Parent 2 ', 0xF800298C66A0 );

SELECT [ts].[SectionId] ,
       [ts].[ParentSectionId] ,
       [ts].[SectionName] ,
       [ts].[h].ToString()
FROM [dbo].[tb_Section] AS [ts]
ORDER BY h

“子N”值的值看起来没有显示任何与超级父级的连接。所以让我们运行以下更新:

UPDATE  [dbo].[tb_Section]
SET     [h] = CONCAT('/686778', h.ToString())
WHERE   [SectionName] LIKE 'Child%'

我们正在变得更好,但现在如果我们再次运行上一步中的选择,结果将根据层次结构深度返回。也就是说,超级父母首先回来,然后是父母,然后是最后的孩子。其中,当你看到层次结构的字符串值仍然有意义;所有父行都具有相同的层次结构值,因此它们将一起排序。

最后一次更新应该可以让您到达目的地:

UPDATE  [dbo].[tb_Section]
SET     [h] = CONCAT(ISNULL(h.ToString(), '/'), [SectionId], '/')

现在,当您再次运行相同的选择时,子层次结构将一起排序。所以TL; DR就是这个,HierarchyID需要提供从源(在你的情况下,Super-Parent)到目的地(在你的情况下,Child)的完整路径,包括在内。