• subscribe
July 30, 2008 12:00 AM

SQL Server 2008’s HierarchyID, Part 2

Move subtrees and convert parent-child representations of hierarchies
SQL Server Pro
InstantDoc ID #99369
Downloads
99369.zip

Executive Summary: SQL Server 2008’s new data type, HIERARCHYID, lets you represent nodes in a hierarchy and query the hierarchy, as well as move a subtree from one parent location in the hierarchy to another with the GetReparentedValue method, and convert a parent-child representation of the hierarchy to a representation that uses the HIERARCHYID data type.

SQL Server 2008’s new data type, called HIERARCHYID, provides powerful new capabilities. For example, you can use HIERARCHYID to represent nodes in a hierarchy and to query the hierarchy, as I demonstrated in last month’s column. (See “HierarchyID,” July 2008, InstantDoc ID 99036.) Another capability you can use is to move a subtree from one parent location in the hierarchy to another by using the GetReparentedValue method. In addition, you can convert a parent-child representation of the hierarchy to a representation that uses the new HIERARCHYID data type. I’ll walk through these capabilities in this month’s column, using the same Employees table that I used in last month’s column. If you’re following along, you’ll want to run the code in Listing 1 to create the Employees table and populate it with sample data.

Moving a Subtree
When I showed how to insert new nodes into the hierarchy last month, I explained how to use the GetDescendant method to calculate a HIERARCHYID value for a new node under an existing node. You might need to handle other types of changes within hierarchies as well. For example, you might need to move a whole subtree of employees from one manager to another (i.e., move an employee and all of his or her subordinates).

To achieve this task you can use the GetReparented- Value method of the HIERARCHYID data type. You invoke this method on the HIERARCHYID value of the node you want to reparent (call it @existing_ node_hid), and provide as inputs the value of the old parent (call it @old_parent_hid) and the value of the new parent (@new_mgr_hid). Here’s the syntax of the GetReparentedValue method:

 @existing_node_hid.GetReparentedValue

  (@old_parent_hid,@new_parent_hid)

Note that this method doesn’t overwrite or change the value in @existing_node_hid. Instead, it returns a new HIERARCHYID value. You must update the target node’s HIERARCHYID value with the new one. Logically, the GetReparentedValue method does something very simple—it substitutes the part of the existing node’s path that represents the old parent’s path with the new parent’s path. If the path of the existing node is /x/a/, the path of the old parent is /x/, and the path of the new parent is /y/, the GetReparentedValue method would return /y/a/. For example, if the path of the existing node is /1/2/1/, the path of the old parent is /1/2/, and the path of the new parent is /2/1/1/4/, the GetReparentedValue method would return /2/1/1/4/1/.

If the target parent already has children, the Get- ReparentedValue method won’t necessarily produce a unique value. If you reparent node /x/a/ from old parent /x/ to new parent /y/, and /y/ already has a child /y/a/, you will get a duplicate value. The trick is that you don’t use the GetReparentedValue method to move a single node from one parent to another; for this task you can simply use the GetDescendant method, and produce a completely new value. You use the GetReparentedValue method when you need to move a whole subtree in conjunction with the GetDescendant method.

Suppose you need to move the whole subtree of employee /x/a/ from old parent /x/ to new parent /y/ that already has children. You achieve this with the following steps:

1. Use the GetDescendant method to produce a completely new HIERARCHYID value under /y/ (call it /y/b/).
2. Update the HIERARCHYID value of all nodes in the subtree of /x/a/ (including self), to the return value of node.GetReparentedValue(/x/a/, /y/b/).

Because /y/b/ is a completely new HIERARCHYID value under the target parent, /y/b/ has no existing children; hence, moving the subtree doesn’t cause conflicting HIERARCHYID values.

Run the code in Listing 2 to create the usp_Reparent stored procedure that implements this logic to move a subtree. The code in the procedure first opens a transaction. Within the transaction, the code retrieves the hid value of the new manager (@new_mgr_hid) and specifies the UPDLOCK hint to obtain an UPDATE lock on the row. Only one transaction can hold an UPDATE lock at a time; once obtained, this lock is kept until the end of the transaction. This way, if simultaneous invocations of usp_Reparent and usp_AddEmp make requests to create a new HIERARCHYID value under the same target manager, those requests won’t produce duplicate values, but instead will be queued.

The code retrieves the hid value of the old manager (@old_root). Next, the code uses the GetDescendant method to produce a new HIERARCHYID value under the new manager (@new_root). Then, the GetReparentedValue method updates the HIERARCHYID values of the employee that is moved and of all descendants of that employee. Finally, the code commits the transaction, releasing all locks.

Continued on page 2



ARTICLE TOOLS

Comments
  • Tim
    4 years ago
    Sep 29, 2008

    Yes, this is clearer now. The other part that was a little confusing was that when it was re-parenting itself, @existing_node_hid is equal to @old_parent_hid it the method syntax:
    @existing_node_hid.GetReparentedValue(@old_parent_hid,@new_parent_hid)

    The variable names used by the GetReparentedValue method and the usp_Reparent procedure, in addition to the "Parent is considered its own descendant" made it somewhat confusing. Thanks for the clarification!

  • meganbearly
    4 years ago
    Aug 27, 2008

    Hi techtwk,
    My name is Megan Bearly, and I'm an associate editor for SQL Server Magazine. I recently passed your question on to Itzik Ben-Gan. The following is his response to your question:

    “Thanks for your question; I’ll try to clarify things.
    Perhaps the confusion has to do with understanding exactly what is moved and where. The usp_Reparent procedure doesn’t move the subtree of @empid excluding self under @new_magrid, rather including self. Therefore, @oldroot is simply the original hid value of the node @empid (not the original hid value of the employee’s parent), and @newroot is the new hid value of @empid. Once the procedure calculates the new hid value of the node whose subtree is to be moved, it reparents all descendents (and as you observed, including self), by providing @oldroot and @newroot.

    Hope this clarifies things.

    Cheers,
    BG”

    Please let me know if you have anymore questions.

    Megan Bearly
    Associate Editor, SQL Server Magazine
    megan.bearly@penton.com

  • Tim
    4 years ago
    Aug 23, 2008

    I have not yet gotten SQL Server 2008, and thus have not tried HierarchyID yet. I was a little confused by these,articles until on MSDN I read "Parent is considered its own descendant." Thus, I can see that the UPDATE statement in the usp_ReParent procedure will update the employee AND employee's descendants.

    However, it seems to me that the following code retrieves the hid value of the employee to be moved and not the hid value of the old manager:

    SET @old_root = (SELECT hid FROM dbo.Employees
    WHERE empid = @empid);

    I would have thougth an hid.GetAncestor(1) would have been needed in that query. What an I missing?

You must log on before posting a comment.

Are you a new visitor? Register Here