callisto
callisto

Reputation: 5083

Updating all HierarchyID nodes in SQL Server 2008

I am importing data from one database to another. The hierarchyid column must also be ported over with the relationships intact.

If the target table is empty, it's easy. When the target contains rows, the hierarchy as it is in the source table will not be valid in the target table

The easiest would be to increment all values in the hierarchy field with the value of the first ported row's primary key.

So if a source row's hierarchyid was '/1/12/13/' and the next available id in target before importing was 101, the hierarchy should change so each value is incremented by 100: '/101/112/113/'

I have read Updating “Hierarchyid” in SQL Server, but do not see how it can be applied to my problem.

How would I go about incrementing each number in each hierarchyid of each row by 1 set value, i.e. increment all numbers in the hierarchy field by 100?

Upvotes: 2

Views: 2744

Answers (1)

Bogdan Sahlean
Bogdan Sahlean

Reputation: 1

Look at this solution (explanations can be found bellow):

DECLARE @Source TABLE
(
    ID HIERARCHYID PRIMARY KEY
    ,Name NVARCHAR(50) NOT NULL
);

DECLARE @Target TABLE
(
    ID HIERARCHYID PRIMARY KEY
    ,Name NVARCHAR(50) NOT NULL
    ,OldID HIERARCHYID
);

INSERT  @Source 
VALUES  
        ('/1/','a'), ('/1/1/','aa'), ('/1/2/','ab'), ('/1/3/','ac')
        ,('/2/','b')
        ,('/3/','c'), ('/3/3/','cc')
        ,('/4/','d'), ('/4/1/','da'), ('/4/2/','db'), ('/4/2/1/','dba'), ('/4/2/1/5/','dbaf');

DECLARE @LastTargetRootNodeAsInteger INT;       
SELECT  @LastTargetRootNodeAsInteger = REPLACE( MAX( a.ID.GetAncestor( a.ID.GetLevel()-1 ) ).ToString(), '/', '')
FROM    @Target a;

WITH CteUpdate
AS
(
SELECT  a.ID 
        ,a.Name
        ,a.ID.GetAncestor( a.ID.GetLevel()-1 ) AS OldRootID
        ,CONVERT(HIERARCHYID,'/'+CONVERT(VARCHAR(256),@LastTargetRootNodeAsInteger+DENSE_RANK()OVER(ORDER BY a.ID.GetAncestor( a.ID.GetLevel()-1 )))+'/') NewRootID
FROM    @Source a
)
INSERT  @Target(ID, Name, OldID)
SELECT  a.ID.GetReparentedValue(a.OldRootID, a.NewRootID), a.Name, a.ID
FROM    CteUpdate a;

SELECT  *
        ,t.ID.ToString() AS CurrentNodeToString
        ,t.OldID.ToString() AS OldNodeToString
FROM    @Target t
ORDER BY t.ID;

Explanation:

  1. First step suppose to find last root node (@LastTargetRootNodeAsInteger) from the target table.
  2. Then, for every ID from source table we get the root node (old root node: a.ID.GetAncestor( a.ID.GetLevel()-1 ) ).
  3. For every old root node we get the new root node ('/'+...+@LastTargetRootNodeAsInteger+DENSE_RANK()OVER(ORDER BY old_root_node)+'/').
  4. And finally, we will insert the rows with the new root node (a.ID.GetReparentedValue(old_root,new_root)).

To see how we get the new IDs, you can execute this query:

SELECT  *
        ,c.ID.GetReparentedValue(c.OldRootNode,c.NewRootNode).ToString() AS NewCurrentNode
FROM
(
        SELECT  *
                ,'/'+CONVERT(VARCHAR(256),@LastTargetRootNodeAsInteger+DENSE_RANK() OVER(ORDER BY b.OldRootNode))+'/' AS NewRootNode
        FROM
        (
                SELECT  *
                        ,a.ID.ToString() AS OldCurrentNode
                        ,a.ID.GetAncestor( a.ID.GetLevel()-1 ).ToString() AS OldRootNode
                FROM    @Source a
        ) b
) c

Results:

results

Upvotes: 3

Related Questions