Showing posts with label HierarchyId Data Type. Show all posts
Showing posts with label HierarchyId Data Type. Show all posts

Sunday, 21 June 2015

PERSISTED Columns with HierarchyId Datatype - SQL Server 2008 by serverku

I think you have already read all the articles related to HierarchyId in the past And hope you understood the concept and usage of it. You also experienced with performance by using HierarchyId datatype as I have posted the performance review in earlier posts as well.

With earlier posts you got some basic methods or functions as how we get the hierarchy data levels, root node, string path. Also, some other methods like as how can we get up-line & down-line modes. Here I am going to present the same, but as Persisted column. So we do not need to write those functions in query level every time. Let us see the workaround for that.

How can ?
We need to create one table and define those columns as a function call as a PERSISTED. We will look the methods to get the hierarchical data without defining columns as Persisted columns and we will use those function calls at the query level. The scripts for the same are as follows.
-- Creating objects
IF (OBJECT_ID('TblHierarchyStructure','U') > 0)
DROP TABLE TblHierarchyStructure
GO

CREATE TABLE TblHierarchyStructure
(
ItemId INT,
ParentItemId INT,
ItemOrder INT,
ItemName VARCHAR(100),
HierarchyNode HIERARCHYID
)

GO

-- Inseting records in tables for the demo
INSERT INTO TblHierarchyStructure
(ItemId,
ParentItemId,
ItemOrder,
ItemName,
HierarchyNode)
SELECT 1,
NULL,
1,
'RootItem',
HierarchyId::Parse('/')
UNION ALL
SELECT 2,
1,
1,
'FirstItem',
HierarchyId::Parse('/1/')
UNION ALL
SELECT 3,
1,
2,
'SecondItem',
HierarchyId::Parse('/2/')
UNION ALL
SELECT 4,
1,
3,
'ThirdItem',
HierarchyId::Parse('/3/')
UNION ALL
SELECT 5,
2,
1,
'FourthItem',
HierarchyId::Parse('/1/1/')
UNION ALL
SELECT 6,
4,
1,
'FifthItem',
HierarchyId::Parse('/3/1/')
UNION ALL
SELECT 7,
5,
1,
'SixthItem',
HierarchyId::Parse('/1/1/1/')
UNION ALL
SELECT 8,
5,
2,
'SeventhItem',
HierarchyId::Parse('/1/1/2/')
UNION ALL
SELECT 9,
5,
3,
'NinthItem',
HierarchyId::Parse('/1/1/3/')
UNION ALL
SELECT 10,
8,
1,
'TenthItem',
HierarchyId::Parse('/1/1/2/1/')

GO

-- Usinf HierarchyId functions at query level and see output.
SELECT *,
HierarchyNode.ToString() AS ItemNodeString,
HierarchyNode.GetLevel() AS ItemNodeLevel,
HierarchyNode.GetAncestor(1) AS ParentNode,
HierarchyNode.GetAncestor(1).ToString() AS ParentNodeString
FROM TblHierarchyStructure
GO

Now we will look the methods to get the hierarchical data with defining columns as Persisted columns and we will use those functions call at the column level. The scripts for the same are as follows.
-- Creating objects
IF (OBJECT_ID('TblHierarchyStructure','U') > 0)
DROP TABLE TblHierarchyStructure
GO

CREATE TABLE TblHierarchyStructure
(
ItemId INT,
ParentItemId INT,
ItemOrder INT,
ItemName VARCHAR(100),
HierarchyNode HIERARCHYID NOT NULL PRIMARY KEY,
ItemNodeString AS HierarchyNode.ToString() PERSISTED,
ItemNodeLevel AS HierarchyNode.GetLevel() PERSISTED,
ParentNode AS HierarchyNode.GetAncestor(1) PERSISTED,
ParentNodeString AS HierarchyNode.GetAncestor(1).ToString()
)

GO

-- Inserting sample records here
INSERT INTO TblHierarchyStructure
(ItemId,
ParentItemId,
ItemOrder,
ItemName,
HierarchyNode)
SELECT 1,
NULL,
1,
'RootItem',
HierarchyId::Parse('/')
UNION ALL
SELECT 2,
1,
1,
'FirstItem',
HierarchyId::Parse('/1/')
UNION ALL
SELECT 3,
1,
2,
'SecondItem',
HierarchyId::Parse('/2/')
UNION ALL
SELECT 4,
1,
3,
'ThirdItem',
HierarchyId::Parse('/3/')
UNION ALL
SELECT 5,
2,
1,
'FourthItem',
HierarchyId::Parse('/1/1/')
UNION ALL
SELECT 6,
4,
1,
'FifthItem',
HierarchyId::Parse('/3/1/')
UNION ALL
SELECT 7,
5,
1,
'SixthItem',
HierarchyId::Parse('/1/1/1/')
UNION ALL
SELECT 8,
5,
2,
'SeventhItem',
HierarchyId::Parse('/1/1/2/')
UNION ALL
SELECT 9,
5,
3,
'NinthItem',
HierarchyId::Parse('/1/1/3/')
UNION ALL
SELECT 10,
8,
1,
'TenthItem',
HierarchyId::Parse('/1/1/2/1/')

GO

-- We have not using HierarchyId functions at query level
-- and using them at columns level as Persisted
SELECT
*
FROM TblHierarchyStructure

GO

I hope you liked this post about Persisted columns with HierarchyID new datatype. Share your experience if you know this type of the usage.

Wednesday, 10 June 2015

Move Node to other place with HierarchyId Data Type - SQL Server 2008 by serverku

After writing some of the posts related HierachyId data type, Finally moving in last topics of HierachyId functions, we will see here the movement of the hierarchy nodes. Let's start from the script to create data for the demo.
-- Create database and table
CREATE DATABASE HierarchyDB

GO

USE HierarchyDB

GO

IF ( Object_id('HierarchyTab') > 0 )
DROP TABLE HierarchyTab

GO

CREATE TABLE HierarchyTab
(
NodeId INT IDENTITY(1, 1)
,NodeDepth VARCHAR(100) NOT NULL
,NodePath HIERARCHYID NOT NULL
,NodeDesc VARCHAR(100)
)

GO

-- Creating constraint on hierarchy data type.
ALTER TABLE HierarchyTab ADD CONSTRAINT U_NodePath UNIQUE CLUSTERED (NodePath)

GO
-- Inserting data in above creatd table.
INSERT INTO HierarchyTab(NodeDepth,NodePath,NodeDesc)
VALUES
('1',HIERARCHYID::Parse('/'),'Node-1'),
('1.1',HIERARCHYID::Parse('/1/'),'Node-2'),
('1.1.1',HIERARCHYID::Parse('/1/1/'),'Node-3'),
('1.1.2',HIERARCHYID::Parse('/1/2/'),'Node-4'),
('1.2',HIERARCHYID::Parse('/2/'),'Node-5'),
('1.2.1',HIERARCHYID::Parse('/2/1/'),'Node-6'),
('1.2.2',HIERARCHYID::Parse('/2/2/'),'Node-7'),
('1.2.2.1',HIERARCHYID::Parse('/2/2/1/'),'Node-8'),
('1.2.2.1.1',HIERARCHYID::Parse('/2/2/1/1/'),'Node-9'),
('1.2.2.1.2',HIERARCHYID::Parse('/2/2/1/2/'),'Node-10'),
('1.3',HIERARCHYID::Parse('/3/'),'Node-11'),
('1.3.1',HIERARCHYID::Parse('/3/1/'),'Node-12'),
('1.3.2',HIERARCHYID::Parse('/3/2/'),'Node-13'),
('1.4',HIERARCHYID::Parse('/4/'),'Node-14')

GO
The logical image of hierarchy data are as following,


(Click on image to enlarge)
Now we will move hierarchy nodes and it's down-line from one place to another place.

But how to move?


GetReparentedValue(OldNode, NewNode) : It will move all nodes, including itself and down-line as well to another place. Let's see  what should be new place of the hierarchy id "1" and it's down-line nodes after moving to another place.
-- GetReparentedValue()
SELECT
NodePath.GetLevel() AS NodeLevel,
NodePath.ToString() AS NodeCurrentPath,
NodePath.GetReparentedValue(HIERARCHYID::Parse('/1/'), HIERARCHYID::Parse('/4/1/')).ToString()
AS NewNodePath,
-- Above line will give new node path of id 1 and it's downline where it will be placed.
NodeId,
NodeDepth,
NodePath,
NodeDesc
FROM HierarchyTab
WHERE NodePath.IsDescendantOf(HIERARCHYID::Parse('/1/')) = 1

GO

(Click on image to enlarge)


Now run the below query and move the id 1 and down-line of hierarchy id and then see a logical image from data
UPDATE HierarchyTab
SET NodePath = NodePath.GetReparentedValue(HIERARCHYID::Parse('/1/'), HIERARCHYID::Parse('/4/1/')),
NodeDepth = '4.' + NodeDepth
WHERE NodePath.IsDescendantOf(HIERARCHYID::Parse('/1/')) = 1

GO

(Click on image to enlarge)

Hope you understood well and get used this feature for the hierarchical data. Please comments how you are using HierarchyId Data Type and their functions.

Tuesday, 9 June 2015

Is Child Node? - With HierarchyId Data Type in SQL Server 2008 by serverku

Various method I have introduced in my earlier posts, like How to get levels of hierarchy nodes, get up-line and down-line of nodes, get string paths of nodes and get next available nodes.

I hope you have read all of them and you liked too. In this post I am presenting how can we know the node is child of particular node or not? Before going ahead to run the script and see the output of hierarchy structure.
-- Create database and table
CREATE DATABASE HierarchyDB

GO

USE HierarchyDB

GO

IF ( Object_id('HierarchyTab') > 0 )
DROP TABLE HierarchyTab

GO

CREATE TABLE HierarchyTab
(
NodeId INT IDENTITY(1, 1)
,NodeDepth VARCHAR(100) NOT NULL
,NodePath HIERARCHYID NOT NULL
,NodeDesc VARCHAR(100)
)

GO

-- Creating constraint on hierarchy data type.
ALTER TABLE HierarchyTab ADD CONSTRAINT U_NodePath UNIQUE CLUSTERED (NodePath)

GO
-- Inserting data in above creatd table.
INSERT INTO HierarchyTab(NodeDepth,NodePath,NodeDesc)
VALUES
('1',HIERARCHYID::Parse('/'),'Node-1'),
('1.1',HIERARCHYID::Parse('/1/'),'Node-2'),
('1.1.1',HIERARCHYID::Parse('/1/1/'),'Node-3'),
('1.1.2',HIERARCHYID::Parse('/1/2/'),'Node-4'),
('1.2',HIERARCHYID::Parse('/2/'),'Node-5'),
('1.2.1',HIERARCHYID::Parse('/2/1/'),'Node-6'),
('1.2.2',HIERARCHYID::Parse('/2/2/'),'Node-7'),
('1.2.2.1',HIERARCHYID::Parse('/2/2/1/'),'Node-8'),
('1.2.2.1.1',HIERARCHYID::Parse('/2/2/1/1/'),'Node-9'),
('1.2.2.1.2',HIERARCHYID::Parse('/2/2/1/2/'),'Node-10'),
('1.3',HIERARCHYID::Parse('/3/'),'Node-11'),
('1.3.1',HIERARCHYID::Parse('/3/1/'),'Node-12'),
('1.3.2',HIERARCHYID::Parse('/3/2/'),'Node-13'),
('1.4',HIERARCHYID::Parse('/4/'),'Node-14')

GO
Here is the logical image of the above data are as follows,


(Click on image to enlarge)

So our topic here, Is the node Child ?

IsDescendantOf() : This functions will return 1 if the node is child of given node, return 0 if the node is not child.

Let's run the script with example as who are child of hierarchy node "1".
SELECT 
NodePath.GetLevel() AS NodeLevel,
NodePath.ToString() AS NodeStringPath,
NodePath.GetAncestor(1).ToString() AS ParentNode,
NodePath.IsDescendantOf(HIERARCHYID::Parse('/1/')) IsParent,
-- Above line will return 1 or 0
NodeId,
NodeDepth,
NodePath,
NodeDesc
FROM HierarchyTab

GO

You can see the above image and check parent node for appropriate child nodes. Hope you like this. Stay tuned for more.


Monday, 8 June 2015

Next Available Node with HierarchyId Data Type - SQL Server 2008 by serverku


I have posted for the some of the functions with examples and demonstrate them in earlier posts. I have written articles of HierarchyId data type overview, some basic functions, even demonstrate for the up-line & down-line of hierarchy nodes which you can read from below links.

Here I will present how can we get the next available node to be planed with HierarchyID data type function. Let's first create hierarchy data structure with the script.
-- Create a database and table
CREATE DATABASE HierarchyDB

GO

USE HierarchyDB

GO

IF ( Object_id('HierarchyTab') > 0 )
DROP TABLE HierarchyTab

GO

CREATE TABLE HierarchyTab
(
NodeId INT IDENTITY(1, 1)
,NodeDepth VARCHAR(100) NOT NULL
,NodePath HIERARCHYID NOT NULL
,NodeDesc VARCHAR(100)
)

GO

-- Creating constraint on hierarchy data type.
ALTER TABLE HierarchyTab ADD CONSTRAINT U_NodePath UNIQUE CLUSTERED (NodePath)

GO
-- Inserting data in above creatd table.
INSERT INTO HierarchyTab(NodeDepth,NodePath,NodeDesc)
VALUES
('1',HIERARCHYID::Parse('/'),'Node-1'),
('1.1',HIERARCHYID::Parse('/1/'),'Node-2'),
('1.1.1',HIERARCHYID::Parse('/1/1/'),'Node-3'),
('1.1.2',HIERARCHYID::Parse('/1/2/'),'Node-4'),
('1.2',HIERARCHYID::Parse('/2/'),'Node-5'),
('1.2.1',HIERARCHYID::Parse('/2/1/'),'Node-6'),
('1.2.2',HIERARCHYID::Parse('/2/2/'),'Node-7'),
('1.2.2.1',HIERARCHYID::Parse('/2/2/1/'),'Node-8'),
('1.2.2.1.1',HIERARCHYID::Parse('/2/2/1/1/'),'Node-9'),
('1.2.2.1.2',HIERARCHYID::Parse('/2/2/1/2/'),'Node-10'),
('1.3',HIERARCHYID::Parse('/3/'),'Node-11'),
('1.3.1',HIERARCHYID::Parse('/3/1/'),'Node-12'),
('1.3.2',HIERARCHYID::Parse('/3/2/'),'Node-13'),
('1.4',HIERARCHYID::Parse('/4/'),'Node-14')

GO
Hierarchy data structure as imaged as below.


(Click on image to enlarge)

How to we find it?

GetDescendant() : This function will give next available node where we can place new node.

1. GetDescendant(NULL,NULL) : will return default next left node.
2. GetDescendant(LeftNode,NULL) : will return right node next to left node.
3. GetDescendant(NULL,RighNode) : will return left node previous to left node.

Run the following script and see the output.
-- GetDescendant()
SELECT
NodePath.GetLevel() AS NodeLevel,
NodePath.ToString() AS NodeStringPath,
NodePath.GetDescendant(NULL,NULL).ToString() AS NextDefaultNode,
-- Above line will get default node.
NodeId,
NodeDepth,
NodePath,
NodeDesc
FROM HierarchyTab

GO


Looking for one hierarchy node id 13,
SELECT 
NodePath.GetLevel() AS NodeLevel,
NodePath.ToString() AS NodeStringPath,
NodePath.GetDescendant(NULL,NULL).ToString() AS NextDefaultNode,
NodePath.GetDescendant(HIERARCHYID::Parse('/3/2/1/'),NULL).ToString() AS NextRightNode,
NodePath.GetDescendant(NULL,HIERARCHYID::Parse('/3/2/2/')).ToString() AS NextLeftNode,
NodeId,
NodeDepth,
NodePath,
NodeDesc
FROM HierarchyTab
WHERE NodeId = 13

GO

(Click on image to enlarge)

This is what I want to share here and hope you like it.


Sunday, 7 June 2015

Up-Line and Down-Line with HierarchyId Data type - SQL Server 2008 by serverku

I have already given HierarchyId datatype overview in my earlier post. Also explained some of HierarchyId functions in post with details as well. Please go through to the overview and some function details which I explained in my previous posts. In this article I am going to demonstrate following items.

1. How to get the up-line nodes?
2. How to get down-line nodes?

Hierarchies functions will give you the answer to all above questions. Let's demonstrate the answers with examples. Before going ahead, I would like to create a hierarchy data structure by following a script.
-- Create database and table
CREATE DATABASE HierarchyDB

GO

USE HierarchyDB

GO

IF ( Object_id('HierarchyTab') > 0 )
DROP TABLE HierarchyTab

GO

CREATE TABLE HierarchyTab
(
NodeId INT IDENTITY(1, 1)
,NodeDepth VARCHAR(100) NOT NULL
,NodePath HIERARCHYID NOT NULL
,NodeDesc VARCHAR(100)
)

GO

-- Creating constraint on hierarchy data type.
ALTER TABLE HierarchyTab ADD CONSTRAINT U_NodePath UNIQUE CLUSTERED (NodePath)

GO
-- Inserting data in above creatd table.
INSERT INTO HierarchyTab(NodeDepth,NodePath,NodeDesc)
VALUES
('1',HIERARCHYID::Parse('/'),'Node-1'),
('1.1',HIERARCHYID::Parse('/1/'),'Node-2'),
('1.1.1',HIERARCHYID::Parse('/1/1/'),'Node-3'),
('1.1.2',HIERARCHYID::Parse('/1/2/'),'Node-4'),
('1.2',HIERARCHYID::Parse('/2/'),'Node-5'),
('1.2.1',HIERARCHYID::Parse('/2/1/'),'Node-6'),
('1.2.2',HIERARCHYID::Parse('/2/2/'),'Node-7'),
('1.2.2.1',HIERARCHYID::Parse('/2/2/1/'),'Node-8'),
('1.2.2.1.1',HIERARCHYID::Parse('/2/2/1/1/'),'Node-9'),
('1.2.2.1.2',HIERARCHYID::Parse('/2/2/1/2/'),'Node-10'),
('1.3',HIERARCHYID::Parse('/3/'),'Node-11'),
('1.3.1',HIERARCHYID::Parse('/3/1/'),'Node-12'),
('1.3.2',HIERARCHYID::Parse('/3/2/'),'Node-13'),
('1.4',HIERARCHYID::Parse('/4/'),'Node-14')

GO
The structure of HierarchyId data looks as follows,

(Clink on image to enlarge)

Now we have time to demonstrate the answers one by one here.

1. How to get the up-line nodes?

GetAncestor(n) : This function will help us to get up-line of the particular hierarchy node. Looking at the script, which will find first up-line node of all hierarchy nodes.
-- GetAncestor(n)
SELECT
NodePath.GetLevel() AS NodeLevel,
NodePath.ToString() AS NodeStringPath,
NodePath.GetAncestor(1).ToString() AS ParentNode,
-- Here 1 is used to get 1st u-line node
-- You can use n to get nth up-line node
NodeId,
NodeDepth,
NodePath,
NodeDesc
FROM HierarchyTab

GO

Please change value to 2 as GetAncestor(n) in place of 1 in script and get the output, You will get more idea.

2. How to get down-line nodes?

IsDescendantOf() : This function will give us the down-line noes of a particular node. Let us run the script and clear our answer. We are viewing a downline node of hierarchy node 1 by script.

-- IsDescendantOf()
SELECT
NodePath.GetLevel() AS NodeLevel,
NodePath.ToString() AS NodeStringPath,
NodeId,
NodeDepth,
NodePath,
NodeDesc
FROM HierarchyTab
WHERE NodePath.IsDescendantOf(HIERARCHYID::Parse('/1/')) = 1
-- Here we have filtered condition to get down-line of node 1.
GO


I hope you are very clear now after demonstrate to how get up-line and down-line of hierarchy node.

Saturday, 6 June 2015

Get Downline and Upline of hierarchical data and Performance review - CTE vs HierarchyId in SQL Server 2008 by serverku

I have already posted for the HierarchyId and CTE (Common Table Expression), also given the comparison review of them for the level and hierarchical order data. I am not saying that HierarchyId is better than CTE or CTE is better then HierarchyId. But it all depends on. You need to practically use them and review the performance of hierarchies and CTE. I am going to show one more demo to find the members with downline and upline.
-- creating database and objects
CREATE DATABASE HierarchyDB
GO

USE HierarchyDB
GO

IF ( Object_id('HierarchyTab') > 0 )
DROP TABLE HierarchyTab
GO

CREATE TABLE HierarchyTab
(
NodeId INT NOT NULL
,NodeParent int
,NodeDepth VARCHAR(100) NOT NULL
,NodePath HIERARCHYID NULL
,NodeLevel as (NodePath.[GetLevel]())
,NodeStringPath as (NodePath.ToString())
,NodeDesc VARCHAR(100)
)
GO

ALTER TABLE HierarchyTab ADD CONSTRAINT U_NodePath UNIQUE CLUSTERED (NodePath)
GO

INSERT INTO HierarchyTab(NodeId,NodeParent,NodeDepth,NodePath,NodeDesc)
VALUES
(1,NULL,'1',HIERARCHYID::Parse('/'),'Node-1'),
(2,1,'1.1',HIERARCHYID::Parse('/1/'),'Node-2'),
(3,2,'1.1.1',HIERARCHYID::Parse('/1/1/'),'Node-3'),
(4,2,'1.1.2',HIERARCHYID::Parse('/1/2/'),'Node-4'),
(5,1,'1.2',HIERARCHYID::Parse('/2/'),'Node-5'),
(6,5,'1.2.1',HIERARCHYID::Parse('/2/1/'),'Node-6'),
(7,5,'1.2.2',HIERARCHYID::Parse('/2/2/'),'Node-7'),
(8,7,'1.2.2.1',HIERARCHYID::Parse('/2/2/1/'),'Node-8'),
(9,8,'1.2.2.1.1',HIERARCHYID::Parse('/2/2/1/1/'),'Node-9')
GO

SELECT
*
FROM HierarchyTab
GO

1. Get down line data using CTE and HierarchyId and compare the execution plan.
-- Using CTE (Not Using NodePath and NodeLevel)
;WITH cteLevels
AS
(
SELECT
NodeId as Node
,NodeParent as Perent
,NodeDepth as Depth
,0 AS [Level]
,NodeDesc as [Desc]
FROM HierarchyTab
WHERE NodeId = 1

UNION ALL

SELECT
NodeId
,NodeParent
,NodeDepth
,[Level] + 1 AS [Level]
,NodeDesc
FROM HierarchyTab
INNER JOIN cteLevels
ON ( NodeParent = Node )
WHERE [Level] + 1 < =3
)

SELECT
*
FROM cteLevels
WHERE [Level] = 3
GO


-- With HierarchyId
DECLARE @DownlineNode HierarchyId =
(SELECT NodePath FROM HierarchyTab WHERE NodeId = 1)

SELECT
NodePath.ToString() AS NodeStringPath,
NodeId,
NodeParent,
NodeDepth,
NodeLevel,
NodeDesc
FROM HierarchyTab
WHERE NodePath.IsDescendantOf(@DownlineNode) = 1
AND NodeLevel = @DownlineNode.GetLevel() + 3
GO
When you run above script you have data and execution plans as follow,



2. Get up line data using CTE and HierarchyId and compare the execution plan.
-- Using CTE (Not Using NodePath and NodeLevel)
;WITH cteLevels
AS
(
SELECT
NodeId as Node
,NodeStringPath as StringPath
,NodeParent as Parent
,NodeDepth as Depth
,0 AS [Level]
,NodeDesc as [Desc]
FROM HierarchyTab
WHERE NodeId = 9


UNION ALL

SELECT
NodeId
,NodeStringPath as StringPath
,NodeParent
,NodeDepth
,[Level] + 1 AS [Level]
,NodeDesc
FROM HierarchyTab
INNER JOIN cteLevels
ON ( NodeId = Parent )
WHERE [Level] + 1 < =3
)

SELECT
*
FROM cteLevels
WHERE [Level] = 3
GO


-- With HierarchyId
SELECT
NodePath.ToString() AS NodeStringPath,
NodeId,
NodePath.GetAncestor(3).ToString() as ParentOn4thPos,
NodeParent,
NodeDepth,
NodeLevel,
NodeDesc
FROM HierarchyTab
WHERE NodeId = 9
GO
The result set and execution plan when you run above script



Hope you like this and share your experience as comments.

Friday, 5 June 2015

Breath First and Depth First Strategy with Hierarchical Data and Performance review - HierarchyId Data Type vs CTE in SQL Server 2008 by serverku

It is very important to check the performance if we use the new features of alternative methods of SQL Server. I have written earlier posts of the HierarchyId and CTE (Common Table expression). Now in this post you can view the usage of HierarchyId and CTE, comparisons between them. Go ahead with the object creation.
  1. Introduction to HierarchyId data type - Amazing feature of SQL Server 2008
  2. HierarchyId DataType in SQL Server 2008 - Get Level and String Path
-- Creating databse and table
CREATE DATABASE HierarchyDB
GO

USE HierarchyDB
GO

IF ( Object_id('HierarchyTab') > 0 )
DROP TABLE HierarchyTab
GO

CREATE TABLE HierarchyTab
(
NodeId INT NOT NULL
,NodeParent int
,NodeDepth VARCHAR(100) NOT NULL
,NodePath HIERARCHYID NULL
,NodeLevel as (NodePath.[GetLevel]())
,NodeDesc VARCHAR(100)
)
GO

ALTER TABLE HierarchyTab
ADD CONSTRAINT U_NodePath UNIQUE CLUSTERED (NodePath)
GO
Inserting demo records in table created above.
INSERT INTO HierarchyTab(NodeId,NodeParent,NodeDepth,NodePath,NodeDesc)
VALUES
(1,NULL,'1',HIERARCHYID::Parse('/'),'Node-1'),
(2,1,'1.1',HIERARCHYID::Parse('/1/'),'Node-2'),
(3,2,'1.1.1',HIERARCHYID::Parse('/1/1/'),'Node-3'),
(4,2,'1.1.2',HIERARCHYID::Parse('/1/2/'),'Node-4'),
(5,1,'1.2',HIERARCHYID::Parse('/2/'),'Node-5'),
(6,5,'1.2.1',HIERARCHYID::Parse('/2/1/'),'Node-6'),
(7,5,'1.2.2',HIERARCHYID::Parse('/2/2/'),'Node-7'),
(8,7,'1.2.2.1',HIERARCHYID::Parse('/2/2/1/'),'Node-8'),
(9,8,'1.2.2.1.1',HIERARCHYID::Parse('/2/2/1/1/'),'Node-9')
GO
Now we will check the usage  and execution plan of the HierarchyId and CTE.

1. First, we will run the scripts for the to get data level by level order. Let’s start with CTE and the query without using a hierarchy node,
-- Using CTE (Not Using NodePath and NodeLevel)
;WITH cteLevels
AS
(
SELECT
NodeId as Node
,NodeParent as Perent
,NodeDepth as Depth
,0 AS [Level]
,NodeDesc as [Desc]
FROM HierarchyTab
WHERE NodeId = 1


UNION ALL

SELECT
NodeId
,NodeParent
,NodeDepth
,[Level] + 1 AS [Level]
,NodeDesc
FROM HierarchyTab
INNER JOIN cteLevels
ON ( NodeParent = Node )

)

select
*
from cteLevels
Order by [Level]
GO


Now running the script to get hierarchy data level by level order using HierarchyId,
-- With HierarchyId
SELECT
NodePath.ToString() AS NodeStringPath,
NodeId,
NodeParent,
NodeDepth,
NodeLevel,
NodeDesc
FROM HierarchyTab
Order by NodeLevel

GO


Before going ahead with next script, we are checking the execution plan of both above script,

2. Getting records of hierarchy in hierarchical order with CTE and HierarchyId.
-- Using CTE (Not Using NodePath and NodeLevel)
;WITH cteLevels
AS
(
SELECT
NodeId as Node
,NodeParent as Perent
,NodeDepth as Depth
,0 AS [Level]
,NodeDesc as [Desc]
,CAST(NodeId AS VARCHAR(MAX)) AS [Order]
FROM HierarchyTab
WHERE NodeId = 1


UNION ALL

SELECT
NodeId
,NodeParent
,NodeDepth
,[Level] + 1 AS [Level]
,NodeDesc
,[Order] + '.' + CAST(NodeId AS VARCHAR(MAX)) AS [Order]
FROM HierarchyTab
INNER JOIN cteLevels
ON ( NodeParent = Node )

)

SELECT
*
FROM cteLevels
ORDER BY [Order]

GO

View the result of hierarchical order data with running below query with HierarhyId,
-- With HierarchyId
SELECT
NodePath.ToString() AS NodeStringPath,
NodeId,
NodeParent,
NodeDepth,
NodeLevel,
NodeDesc
FROM HierarchyTab
Order by NodePath

GO

Finally, we go through the performance and the execution plan review of both above scripts,


This is just the details of execution and performance review.