In one table I have two columns like below
ID ParentID
1 0x0
2 1
3 2
9 0x0
5 9
6 5
25 0x0
30 25
How to get top level parent ID
You can use a recursive CTE:
WITH CTE AS
(
SELECT *, 1 [Level]
FROM dbo.YourTable
UNION ALL
SELECT A.ID, B.[Parent ID], [Level] + 1
FROM CTE A
INNER JOIN dbo.YourTable B
ON A.[Parent ID] = B.Id
), CTE2 AS
(
SELECT *, RN = ROW_NUMBER() OVER(PARTITION BY ID ORDER BY [Level] DESC)
FROM CTE
WHERE [Parent ID] <> 0
)
SELECT ID, [Parent ID] [Top Level Parent Id]
FROM CTE2
WHERE RN = 1
OPTION (MAXRECURSION 0)
;
Here is a link with a demo.
The results are:
╔════╦═════════════════════╗
║ ID ║ Top Level Parent Id ║
╠════╬═════════════════════╣
║ 2 ║ 1 ║
║ 3 ║ 1 ║
║ 5 ║ 9 ║
║ 6 ║ 9 ║
║ 30 ║ 25 ║
╚════╩═════════════════════╝
Yet another shorter CTE :
WITH cte AS(
SELECT *, id AS topparent
FROM t
WHERE parentid IS NULL
UNION ALL
SELECT t.*, c.topparent
FROM t JOIN cte c ON c.id = t.parentid
WHERE t.id <> t.parentid
)
SELECT * FROM cte
Tested here : SQL Fiddle
EDIT : This damn SQLfiddle is unresponsive again. Test DDL Code below
CREATE table t (id INT, parentid INT);
INSERT INTO T VALUES (1 , NULL );
INSERT INTO T VALUES (2 , 1 );
INSERT INTO T VALUES (3 , 2 );
INSERT INTO T VALUES (9 , NULL );
INSERT INTO T VALUES (5 , 9 );
INSERT INTO T VALUES (6 , 5 );
INSERT INTO T VALUES (25 , NULL );
INSERT INTO T VALUES (30 , 25 );
If the top-level node always follows a single path to a single leaf node, then the desired result can be achieved by combining Breadcrumbs and recursive CTEs. The following example demonstrates the approach:
CREATE TABLE #Items
(
ID INT
,[ParentID] INT
)
INSERT INTO #Items
VALUES
(1, 0x0),
(2, 1),
(3, 2),
(9, 0x0),
(5, 9),
(6, 5),
(25, 0x0),
(30, 25)
;WITH CTE_Breadcrumbs
AS
(
SELECT ID
,ParentID
,CAST(ID AS NVARCHAR(1000)) AS [Path]
,LEN(CAST(ID AS NVARCHAR(1000))) AS [PathLength]
,ID AS [RootNodeID]
FROM #Items
WHERE ParentID = 0x0
UNION ALL
SELECT Child.ID
,Child.ParentID
,CAST(Parent.[Path] + N'\' + CAST(Child.ID AS NVARCHAR(1000)) AS NVARCHAR(1000)) AS [Path]
,LEN(CAST(Parent.[Path] + N'\' + CAST(Child.ID AS NVARCHAR(1000)) AS NVARCHAR(1000))) AS [PathLength]
,CAST(
CASE
WHEN CHARINDEX('\', [Path]) > 0 THEN LEFT([Path], CHARINDEX('\', [Path]) - 1)
ELSE Child.ParentID
END
AS INT) AS [RootNodeID]
FROM #Items Child
INNER JOIN CTE_Breadcrumbs Parent ON Child.ParentID = Parent.ID
)
,
CTE_FullPathID
AS
(
SELECT [RootNodeID]
,MAX([PathLength]) AS [PathFullLength]
FROM CTE_Breadcrumbs
GROUP BY [RootNodeID]
)
SELECT CTE_Breadcrumbs.RootNodeID
,CAST(
REVERSE(
CASE
WHEN CHARINDEX('\', REVERSE([Path])) > 0 THEN LEFT(REVERSE([Path]), CHARINDEX('\', REVERSE([Path])) - 1)
ELSE NULL
END
)
AS INT) AS [LeafNodeID]
,CTE_Breadcrumbs.[Path]
FROM CTE_Breadcrumbs
INNER JOIN CTE_FullPathID ON CTE_Breadcrumbs.[RootNodeID] = CTE_FullPathID.[RootNodeID] AND CTE_Breadcrumbs.PathLength = CTE_FullPathID.[PathFullLength]