Skip to content

Get Tree Path Function

Let's say you have a table called [dbo].[Hierarchy] with the following data:

ID ParentID Name
1 0 Account Types
2 0 Card Types
3 1 Savings Account
4 1 Checking Account
5 1 Money Market Account
6 1 Certificate of Deposits
7 2 ATM Card
8 2 Charge Card
9 2 Credit Card
10 2 Debit Card

Given this table, you want to create the following output:

Path Name
Account Types Account Types
Card Types Card Types
Account Types/Savings Account Savings Account
Account Types/Checking Account Checking Account
Account Types/Money Market Account Money Market Account
Account Types/Certificate of Deposits Certificate of Deposits
Card Types/ATM Card ATM Card
Card Types/Charge Card Charge Card
Card Types/Credit Card Credit Card
Card Types/Debit Card Debit Card

If the table only contains one level of hierarchy, this can be accomplished easily with the following statement:

SELECT ISNULL(B.[Name] + '/', '') + A.[Name] AS [Path], A.[Name]
FROM [dbo].[Hierarchy] A LEFT OUTER JOIN [dbo].[Hierarchy] B
                                      ON A.[ParentID] = B.[ID]

What if there's another level in the data, making it two levels as such:

ID ParentID Name
1 0 Account Types
2 0 Card Types
3 1 Savings Account
4 1 Checking Account
5 1 Money Market Account
6 1 Certificate of Deposits
7 2 ATM Card
8 2 Charge Card
9 2 Credit Card
10 2 Debit Card
11 9 Discover
12 9 Mastercard
13 9 Visa
Path Name
Account Types Account Types
Card Types Card Types
Account Types/Savings Account Savings Account
Account Types/Checking Account Checking Account
Account Types/Money Market Account Money Market Account
Account Types/Certificate of Deposits Certificate of Deposits
Card Types/ATM Card ATM Card
Card Types/Charge Card Charge Card
Card Types/Credit Card Credit Card
Card Types/Debit Card Debit Card
Card Types/Credit Card/Discover Discover
Card Types/Credit Card/Mastercard Mastercard
Card Types/Credit Card/Visa Visa

This can be accomplished by the following query:

SELECT ISNULL(C.[Name] + '/', '') + ISNULL(B.[Name] + '/', '') + A.[Name], A.[Name]
FROM [dbo].[Hierarchy] A LEFT OUTER JOIN [dbo].[Hierarchy] B
                                      ON A.[ParentID] = B.[ID]
                         LEFT OUTER JOIN [dbo].[Hierarchy] C
                                      ON B.[ParentID] = C.[ID]

These queries are useful if you know how many levels the table has. What if you don't know the number of levels your table have? What if your table goes as deep as 10 levels? What can you do now?

Here's a useful user-defined function that can perform the task of generating the path of the current row.

CREATE FUNCTION [dbo].[ufn_GetParentPath] ( @pCurrentNodeID    INT )
RETURNS VARCHAR(1000)
AS
BEGIN

    DECLARE @vCurrentNodeName     VARCHAR(50)
    DECLARE @vParentID            INT

    IF @pCurrentNodeID IS NULL OR @pCurrentNodeID = 0
        RETURN NULL

    SELECT @vCurrentNodeName = [Name], @vParentID = [ParentID]
    FROM [dbo].[Hierarchy]
    WHERE [ID] = @pCurrentNodeID

    RETURN ISNULL([dbo].[ufn_GetParentPath] ( @vParentID ) + '/', '') + @vCurrentNodeName

END
GO

To use this user-defined function to return the desired output, simply issue the following SELECT statement:

SELECT [dbo].[ufn_GetParentPath] ( [ID] ) AS [Path], [Name]
FROM [dbo].[Hierarchy]

This function is straight-forward in the sense that it simply concatenates the name of the current node with the name of its parent. The trick here is in the following code:

RETURN ISNULL([dbo].[ufn_GetParentPath] ( @vParentID ) + '/', '') + @vCurrentNodeName

This step is returning the name of the parent concatenated with the name of the current node. The function gets the name of the parent node by calling itself. This kind of function is called a recursive function, which is a function that calls itself. The function stops calling itself when the current node is already NULL or 0 and simply returns a NULL value.

This user-defined function can handle up to 31 levels of hierarchy. This limitation is more of a limitation of SQL Server than the limitation of the function itself because SQL Server has a maximum of 32 nesting levels when calling functions or stored procedures. Nesting occurs when one function calls another function, which is the case in this function.