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

时间:2015-02-10 03:17:17

标签: sql-server sql-server-2008 hierarchyid

我在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哪个是这个特定节点的兄弟节点。哪个可以。但是,我想要的是在PARTICULAR父级下的树中的任意位置插入节点。这意味着我希望能够使用类似的东西:

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

这意味着

  • 只要在正确的父节点下插入节点,我们就不关心节点的水平定位
  • 当我为同一个父项的多次插入尝试GetDescendant(NULL,NULL)时,它为每个子项提供相同的路径/ 1 /。那是为什么?
  • 另外,我遇​​到了以下链接:https://technet.microsoft.com/en-us/library/bb677212%28v=sql.105%29.aspx。其中显示了为特定父级存储最后插入的子级的示例,然后在插入对DB进行任何进一步插入之前将其用作参考。它是用于在具有层次结构的表中插入以获得路径中唯一性的标准方法吗?

2 个答案:

答案 0 :(得分:2)

  

当我为同一个父项的多次插入尝试GetDescendant(NULL,NULL)时,它为每个子项提供相同的路径/ 1 /。那是为什么?

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

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

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

示例中要注意的是,我在整个布料中创建了@a和@b(也就是说,我没有使用GetDescendant方法创建@b)。 GetDescendant方法的参数的要点是,它知道您希望放置在兄弟姐妹列表中的位置。如果你不关心(似乎你没有根据你的评论),第二个参数将始终为null(这就是说“在广度优先遍历中使新条目成为列表中的最后一个” )。

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

  

它是否是使用层次结构插入表中以获得路径唯一性的标准方法?

这对我来说似乎很合理。我这样思考:我将调用GetDescendant,第一个参数是广度优先遍历中的最后一个现有的直接后代(如果没有现有的后代,可能为NULL),第二个参数为NULL (因为我只是把它写到最后)。

答案 1 :(得分:0)

出于所有常见原因,我不太喜欢游标。但是,使用基于集的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