如何在不必指定左右兄弟的情况下插入唯一的 HierarchyId 路径?

How can I insert unique HierarchyId path without having to specify left and right sibling?

我在 SQL 中使用 HierarchyId 来存储数据。我正在关注这里的教程:

http://www.codeproject.com/Tips/740553/Hierarchy-ID-in-SQL-Server

示例中提到的示例明确指定了节点的位置:

DECLARE @parent HierarchyId = (SELECT Node FROM H WHERE Name = 'Thuru')
DECLARE @jhony HierarchyId = (SELECT Node FROM H WHERE name = 'Johnny')
INSERT INTO H (Node,ID,Name) VALUES (@parent.GetDescendant(@jhony,NULL), 3, 'Robert') 

代码告诉 SQl哪些是这个特定节点的兄弟节点。没关系。但是,我只想在特定父级下的树中的任何位置插入节点。这意味着我希望能够使用类似的东西:

DECLARE @parent HierarchyId = HierarchyId::GetRoot()
INSERT INTO H (Node,ID,Name) VALUES (@parent.GetDescendant(NULL,NULL),2,'Johnny') 

这意味着

When I tried GetDescendant(NULL,NULL) for multiple inserts for the same parent, it gives the same path /1/ to every child. Why is that?

给定的 HierarchyId 实例不会跟踪它拥有的所有后代。事实上,我可以做类似下面的事情:

declare @a hierarchyid = '/1/', @b hierarchyid = '/1/1/';

select @b.IsDescendantOf(@a); --should be 1

示例中要注意的是我创建了@a 和@b out of whole cloth(也就是说,我没有使用GetDescendant 方法创建@b)。 GetDescendant 方法的参数的要点是它知道您想将您的兄弟姐妹放在兄弟姐妹列表中的什么位置。如果您不在乎(而且根据您的评论看来您并不关心),第二个参数将始终为 null(即 "make the new entry the last one in the list in a breadth-first traversal")。

所有这一切都是一种冗长的说法,如果您为 both 参数传递 NULL,它将假设当前在该特定实例下没有后代HierarchyId 的,因此您要求的将是第一个。另一种思考方式是 GetDescendant 方法是确定性的(也就是说,给定相同的参数,它每次都会 return 相同的答案)。

Is it the standart method for doing insert into a table with hierarchy to get the uniqueness in the path?

我觉得很有道理。我这样想:我要调用 GetDescendant ,第一个参数是广度优先遍历中最后一个现有的直接后代(如果没有现有后代,则可能为 NULL),第二个参数为 NULL (因为我只是把它添加到最后)。

出于所有常见原因,我不太喜欢游标。但是,使用基于集合的 INSERT INTO..SELECT 对 IDENTITY 列和 SEQUENCES 工作正常,但对 HIERARCHYID 无效。因此,我使用这种方法多次插入到具有共同父级的层次结构中。它可以在你有多个级别的地方级联。

-- This would add all employees who are managers from an 
-- Employee table (with employee_id and isManager columns)
-- as descendants of an existing root node in an OrgChart table

BEGIN TRAN

DECLARE @root hierarchyid
DECLARE @lastNode hierarchyid
DECLARE @employee_id INT 

SELECT @root = hierarchyid::GetRoot() FROM [dbo].[OrgChart]
SELECT @lastNode = NULL -- GetDescendant(NULL, NULL) for the first descendant

-- Have to use a cursor because using set based INSERT INTO..SELECT
-- with hierarchy gives each row the same hierarchyid

DECLARE c CURSOR FOR 
SELECT employee_id 
FROM [dbo].[Employees]
WHERE
    [isManager] = 1 

OPEN c  
FETCH NEXT FROM c INTO @employee_id 

WHILE @@FETCH_STATUS = 0  
BEGIN  
    INSERT INTO
        [dbo].[OrgChart](
            orgID,
            [effective_start_date], 
            effective_end_date, 
            employee_id
            )
    SELECT
        @root.GetDescendant(@lastNode, NULL),
        GETDATE(),
        NULL,
        @employee_id

    -- Get the hierarchyid you have just added 
    -- so you can add the next one after it
    SELECT @lastNode = orgID FROM [dbo].[OrgChart]

    FETCH NEXT FROM c INTO @employee_id
END 

CLOSE c  
DEALLOCATE c 

COMMIT