Recursive CTE listing all ancestors as columns - sql

I found many examples that show recursive CTEs that will list the path as a concatenated string and the depth of the level. For example:
CREATE TABLE #Test
(ID varchar(20),
ParentID varchar(20)
)
INSERT INTO #Test values ('100000','HEAD');
INSERT INTO #Test values ('100100','100000');
INSERT INTO #Test values ('100200','100100');
INSERT INTO #Test values ('100300','100200');
INSERT INTO #Test values ('100400','100300');
;WITH CTE AS
(
SELECT ID, ParentID, cast(ID as varchar(100)) as path, 0 AS Level
FROM #Test
WHERE ParentID = 'HEAD'
UNION ALL
SELECT t.Id,t.ParentID, cast(cte.path +'>'+ t.ID as varchar(100)), Level + 1
FROM #Test t
INNER JOIN CTE ON t.ParentID = CTE.ID
)
SELECT * FROM CTE
DROP table #Test
Is there a way to return each ancestor in each row in a separate column? So instead of the current result:
ID ParentId Path Level
100000 HEAD 100000 0
100100 100000 100000>100100 1
100200 100100 100000>100100>100200 2
100300 100200 100000>100100>100200>100300 3
100400 100300 100000>100100>100200>100300>100400 4
It would be:
Id Parent L0 L1 L2 L3 L4
100000 HEAD 100000 Null Null Null Null
100100 100000 100000 100100 Null Null Null
100200 100100 100000 100100 100200 Null Null
100300 100200 100000 100100 100200 100300 Null
100400 100300 100000 100100 100200 100300 100400
Assumption is that I know the max level of depth, in this example the max level is 5. SQL Server ver. 12.0, and DB Compatibility is at 120.

If you have a known or maximum number of levels, consider a bit of JSON.
If not 2016+ ... there is a similar XML approach.
Example
;WITH CTE AS
(
SELECT ID, ParentID, cast(ID as varchar(100)) as path, 0 AS Level
FROM #Test
WHERE ParentID = 'HEAD'
UNION ALL
SELECT t.Id,t.ParentID, cast(cte.path +'>'+ t.ID as varchar(100)), Level + 1
FROM #Test t
INNER JOIN CTE ON t.ParentID = CTE.ID
)
SELECT A.ID
,A.ParentID
,B.*
,A.Level
FROM CTE A
Cross Apply (
Select L0 = trim(JSON_VALUE(S,'$[0]'))
,L1 = trim(JSON_VALUE(S,'$[1]'))
,L2 = trim(JSON_VALUE(S,'$[2]'))
,L3 = trim(JSON_VALUE(S,'$[3]'))
,L4 = trim(JSON_VALUE(S,'$[4]'))
,L5 = trim(JSON_VALUE(S,'$[5]'))
From ( values ( '["'+replace(path,'>','","')+'"]' ) ) A(S)
) B
Returns
UPDATE: The XML Approach
;WITH CTE AS
(
SELECT ID, ParentID, cast(ID as varchar(100)) as path, 0 AS Level
FROM #Test
WHERE ParentID = 'HEAD'
UNION ALL
SELECT t.Id,t.ParentID, cast(cte.path +'>'+ t.ID as varchar(100)), Level + 1
FROM #Test t
INNER JOIN CTE ON t.ParentID = CTE.ID
)
SELECT A.ID
,A.ParentID
,B.*
,A.Level
FROM CTE A
Cross Apply (
Select L0 = xDim.value('/x[1]','varchar(50)')
,L1 = xDim.value('/x[2]','varchar(50)')
,L2 = xDim.value('/x[3]','varchar(50)')
,L3 = xDim.value('/x[4]','varchar(50)')
,L4 = xDim.value('/x[5]','varchar(50)')
,L5 = xDim.value('/x[6]','varchar(50)')
From (Select Cast('<x>' + replace(Path,'>','</x><x>')+'</x>' as xml) as xDim) as A
) B

as everyone mentioned you need to know number of levels you have , but here is the implementation with PIVOT:
;WITH CTE AS
(
SELECT ID, ParentID, cast(ID as varchar(100)) as path, 0 AS Level
FROM #Test
WHERE ParentID = 'HEAD'
UNION ALL
SELECT t.Id,t.ParentID, cast(cte.path +'>'+ t.ID as varchar(100)), Level + 1
FROM #Test t
INNER JOIN CTE ON t.ParentID = CTE.ID
)
select
*
from (
Select Id , ParentID,
'L'+ CAST(Row_Number() Over (Partition By ID Order By ID) AS VARCHAR)AS Col,
Split.value
, level
From CTE
Cross apply string_split(path,'>') as Split
) AS tbl
Pivot (Max(value) For Col IN ([L1],[L2],[L3],[L4],[L5])) AS Pvt

Related

How to loop through table using while loop and create another table with values needed

I have two tables: MainTable and MyTable. MyTable has unique ControlNo and ID. I need to add very first EffDate from MainTable to MyTablebased on ID and ControlNo.
For that I need to look at PreviousID column, then see if that PreviousID is in ID column and so on.
Desired output should look like this:
The below is an example with dummy data of getting proper EffDate by supplying an ID value. It works, but how can I loop through the whole MainTable, retrieve ID's and EffDate into separate table, then join that table to MyTable?
-- function returns PreviousID based on ID
CREATE FUNCTION [dbo].[GetPriorQuoteID](#ID varchar(50))
RETURNS varchar(50)
AS
BEGIN
DECLARE #RetVal varchar(50)
SET #RetVal = NULL
SELECT TOP 1 #RetVal = MainTable.PreviousID
FROM MainTable
WHERE MainTable.ID = #ID
RETURN #RetVal
END
-- create sample table
IF OBJECT_ID('MainTable') IS NOT NULL DROP TABLE MainTable;
select 3333 as ControlNo, 'QuoteID3' as ID, 'QuoteID2' as PreviousID, '2020-08-25' as EffDate
into MainTable
union all select 2222 as COntrolNo, 'QuoteID2', 'QuoteID1', '2019-08-25'
union all select 1111 as COntrolNo, 'QuoteID1', NULL, '2018-08-25'
union all select 7777 as COntrolNo, 'QuoteID6', 'QuoteID5', '2020-02-10'
union all select 6666 as COntrolNo, 'QuoteID5', NULL, '2019-02-10'
select * from MainTable
DECLARE #PriorQuote varchar(50)
DECLARE #RetVal VARCHAR(50) = ''
DECLARE #ControlNo INT
DECLARE #ID varchar(50) = 'QuoteID3'
SELECT TOP 1 #ControlNo = MainTable.ControlNo FROM MainTable WHERE MainTable.ID = #ID
Set #PriorQuote = #ID
SELECT TOP 1 #PriorQuote = MainTable.ID FROM MainTable WHERE MainTable.ControlNo = #ControlNo
WHILE dbo.GetPriorQuoteID(#PriorQuote) IS NOT NULL AND dbo.GetPriorQuoteID(#PriorQuote)<> #PriorQuote
BEGIN
SET #PriorQuote = dbo.GetPriorQuoteID(#PriorQuote)
END
SELECT TOP 1 #RetVal = CONVERT(VARCHAR(10), MainTable.EffDate, 101)
FROM MainTable
WHERE MainTable.ID = #PriorQuote
SELECT #RetVal
-- clean up
drop table MainTable
drop function GetPriorQuoteID
UPDATE: Adding dummy data tables
-- create sample table #MainTable
IF OBJECT_ID('tempdb..#MainTable') IS NOT NULL DROP TABLE #MainTable;
create table #MainTable (ControlNo int, ID varchar(50), PreviousID varchar(50), EffDate date)
insert into #MainTable values
(3333,'QuoteID3','QuoteID2', '2020-08-25'),
(2222,'QuoteID2','QuoteID1', '2019-08-25'),
(1111,'QuoteID1',NULL, '2018-08-25'),
(7777,'QuoteID6','QuoteID5', '2020-02-10'),
(6666,'QuoteID5',NULL, '2019-02-10')
--select * from #MainTable
-- create sample table #MyTable
IF OBJECT_ID('tempdb..#MyTable') IS NOT NULL DROP TABLE #MyTable;
create table #MyTable (ControlNo int, ID varchar(50), EffDate date)
insert into #MyTable values
(3333,'QuoteID3',NULL),
(7777,'QuoteID6',NULL)
--select * from #MyTable
You can use a recursive query to traverse the hierarchy.
I would start by joining the original table with the main table, which restricts the paths to just the rows we are interested in. Then, you can recurse towards the parent. Finally, we need to filter on the top parent per path: top() and row_number() come handy for this.
Consider:
with cte as (
select t.controlno, t.id, m.previousid, m.effdate, 1 lvl
from #maintable m
inner join #mytable t on t.controlno = m.controlno and t.id = m.id
union all
select c.controlno, c.id, m.previousid, m.effdate, c.lvl + 1
from cte c
inner join #maintable m on m.id = c.previousid
)
select top(1) with ties controlno, id, effdate
from cte
order by row_number() over(partition by controlno, id order by lvl desc)
Demo on DB Fiddle:
controlno | id | effdate
--------: | :------- | :---------
3333 | QuoteID3 | 2018-08-25
7777 | QuoteID6 | 2019-02-10
using CTE like below you can get the desired results.
See live demo
Learn more about recursive CTEs here
; with cte as
(
select EffDate, ControlNo, ID, Level=1 from MainTable
where PreviousID is NULL
union all
select C.EffDate, M.ControlNo, M.ID, Level=Level+1 from MainTable AS M
join cte as C on C.ID=M.PreviousID
)
select MyTable.*,cte.EffDate from cte join MyTable on Mytable.ID=cte.ID
You can use a recursive CTE for this:
WITH cte
AS
(
SELECT m.ID,m.PreviousID
FROM MainTable m
JOIN MainTable m2
ON m.previousID = m2.ID
WHERE m2.previousID IS NULL
UNION ALL
SELECT m2.ID,cte.previousID
FROM cte
JOIN MainTable m2
ON m2.previousID = cte.ID
)
SELECT *
FROM cte;
Here is a working example of the CTE approach with the table provided
;with recur_cte(ControlNo, ID, PreviousID, EffDate, HLevel) as (
select mt.ControlNo, cast(null as varchar(100)), mt.PreviousID, mt.EffDate, 1
from MainTable mt
where not exists(select 1
from MainTable mt_in
where mt.ID=mt_in.PreviousID)
union all
select rc.ControlNo, rc.ID, mt.PreviousID, mt.EffDate, rc.HLevel+1
from recur_cte rc
join MainTable mt on rc.PreviousID=mt.ID and rc.EffDate>mt.EffDate)
select * from recur_cte;
Results
ControlNo ID PreviousID EffDate HLevel
3333 NULL QuoteID2 2020-08-25 1
7777 NULL QuoteID5 2020-02-10 1
7777 NULL NULL 2019-02-10 2
3333 NULL QuoteID1 2019-08-25 2
3333 NULL NULL 2018-08-25 3

TSQL create ragged hierarchy from flat structure

Consider this table:
declare #table as table (
GL_Group_Code varchar(8),
GL_Branch_Code varchar(8),
Account_Number varchar(4),
GL_Centre_Number varchar(6)
)
insert into #table
select '0100', '0110', '1200', 'S10118' union
select '0100', '0110', '1580', 'S16053' union
select '0200', '0210', '2050', '200049' union
select '0200', '0210', '2050', '782844'
The desired outcome is a parent child relationship across the four source columns into:
declare #table_hier as table (
Parent_ID varchar(8),
Item_ID varchar(8)
)
With a relationship order of column 1,2,3,4 the result for the first row from the source table would look like:
null 0100
0100 0110
0110 1200
1200 S10118
The second row would be:
null 0100
0100 0110
0110 1580
1580 S16053
The relationship order can vary: 1,2,4,3 or 3,4 or 1,3,4
I'm thinking recursive CTE however late on a Friday I could do with little help.
IF you can have id in your table and use table variable to store columns you want to get, you can do this:
declare #ord table (id int)
insert into #ord
values (1), (2), (4)
;with cte as (
select
t.id,
c.value,
row_number() over(partition by t.id order by c.id) as rn
from #table as t
cross apply (
values
(1, t.GL_Group_Code),
(2, t.GL_Branch_Code),
(3, t.Account_Number),
(4, t.GL_Centre_Number)
) as c(id, value)
where
exists (select * from #ord as o where o.id = c.id)
)
insert into #table_hier
select
c1.value as Parent_ID, c2.value as Item_ID
from cte as c1
inner join cte as c2 on c2.rn = c1.rn + 1 and c2.id = c1.id
order by c1.rn
insert into #table_hier
select null, t.Parent_ID
from #table_hier as t
where
not exists
(
select *
from #table_hier as t2
where t2.Item_ID = t.Parent_ID
)
select * from #table_hier
sql fiddle demo
Or if you can store your data in the tables or temporary tables, you can use dynamic SQL solution:
declare #ord table (id int)
declare #stmt nvarchar(max)
insert into #ord
values (1), (2), (4)
;with cte as (
select c.name, row_number() over(order by c.colid) as rn
from sys.syscolumns as c
where
c.id = object_id('dbo.table1') and
exists (select * from #ord as o where o.id = c.colid)
)
select #stmt =
isnull(#stmt + ', ', '') +
'(' + c1.name + ', ' + c2.name + ')'
from cte as c1
inner join cte as c2 on c2.rn = c1.rn + 1
order by c1.rn
select #stmt = '
insert into table_hier1
select C.Parent_ID, C.Item_ID
from table1 as t
cross apply (
values ' + #stmt + '
) as C(Parent_ID, Item_ID)
'
exec sp_executesql
#stmt = #stmt
insert into table_hier1
select null, t.Parent_ID
from table_hier1 as t
where
not exists
(
select *
from table_hier1 as t2
where t2.Item_ID = t.Parent_ID
)
select * from table_hier1
sql fiddle demo
try something like this
declare #table as table (
GL_Group_Code varchar(8),
GL_Branch_Code varchar(8),
Account_Number varchar(4),
GL_Centre_Number varchar(6)
)
insert into #table
select '0100', '0110', '1200', 'S10118' union
select '0100', '0110', '1580', 'S16053' union
select '0200', '0210', '2050', '200049' union
select '0200', '0210', '2050', '782844'
declare #table_hier as table (
Parent_ID varchar(8),
Item_ID varchar(8)
)
DECLARE #RelatonShip VARCHAR(4)
SET #RelatonShip = '134'
IF #RelatonShip = '1234'
BEGIN
INSERT INTO #table_hier ( Parent_ID, Item_ID )
SELECT GL_Group_Code, GL_Branch_Code FROM #table
UNION ALL SELECT GL_Branch_Code, Account_Number FROM #Table
UNION ALL SELECT Account_Number, GL_Centre_Number FROM #table
END
IF #RelatonShip = '1243'
BEGIN
INSERT INTO #table_hier ( Parent_ID, Item_ID )
SELECT GL_Group_Code, GL_Branch_Code FROM #table
UNION ALL SELECT GL_Branch_Code, GL_Centre_Number FROM #Table
UNION ALL SELECT GL_Centre_Number, Account_Number FROM #table
END
IF #RelatonShip = '34'
BEGIN
INSERT INTO #table_hier ( Parent_ID, Item_ID )
SELECT Account_Number, GL_Centre_Number FROM #table
END
IF #RelatonShip = '134'
BEGIN
INSERT INTO #table_hier ( Parent_ID, Item_ID )
SELECT GL_Group_Code, Account_Number FROM #table
UNION ALL SELECT Account_Number, GL_Centre_Number FROM #table
END
SELECT * FROM #table_hier

Select item type and its base type in SQL Server?

I have a simple table :
DECLARE #t TABLE(item INT, itemType INT )
insert INTO #t SELECT 1000,0
insert INTO #t SELECT 1000,3
insert INTO #t SELECT 1000,5
insert INTO #t SELECT 1000,6
insert INTO #t SELECT 2000,0
insert INTO #t SELECT 2000,3
insert INTO #t SELECT 2000,5
insert INTO #t SELECT 2000,6
insert INTO #t SELECT 3000,0
insert INTO #t SELECT 3000,10
insert INTO #t SELECT 4000,11
I want to select all items where itemtype = 3 but if there is a row , provide also its base itemtype (if it exists) which is itemType = 0.
For example :
for itemType = 3
1000,0 should be provided --why ? because table also has 1000 + itemType 0
1000,3 should be provided --why ? because we looked for itemType=3
2000,0 should be provided --why ? because table has found 2000,3 and this 2000 also has itemType=0
2000,3 should be provided --why ? because we looked for itemType=3
for itemType = 10
3000,0 should be provided --why ? because table has found 3000,10 and this 3000 also has itemType=0
3000,10 should be provided --why ? because we looked for itemType=10
for itemType = 11
4000,11 should be provided --why ? because we looked for itemType=11 ( notice , there isn't itemType 0 , so only itself).
I started doing :
;with cte as(
SELECT * FROM #t
)
select * from cte where itemType=3
In summary, if the itemType is found, provide itself + its zero type (if exists), and also for his siblings ( sample(#1) )
But I can't do union here cause CTE is not recognized there... rubbish. it is possible.
How can I solve it ?
SQL ONLINE
To avoid evaluating the itemType = 3 query twice you can self outer join then use CROSS APPLY ... VALUES to UNPIVOT
DECLARE #itemType INT = 3;
WITH T(item1, itemType1, item2, itemType2 )
AS (SELECT *
FROM #t T1
LEFT JOIN #t T2
ON T1.item = T2.item
AND T2.itemType = 0
AND T1.itemType <> 0
WHERE T1.itemType = #itemType)
SELECT item,
itemType
FROM T
CROSS APPLY ( VALUES (item1, itemType1),
(item2, itemType2) ) v(item, itemType)
WHERE item IS NOT NULL
SQL Fiddle
Execution Plans
DECLARE #findtype INT = 3;
WITH results AS
(
SELECT t.item, #findtype
FROM #t t
WHERE t.itemType = #findtype
UNION ALL
SELECT t.item, 0
FROM #t t
INNER JOIN results r on r.item = t.item
WHERE t.itemType = 0
)
SELECT *
FROM results;
WITH recordList
AS
(
SELECT item, itemType
FROM SampleTable
WHERE itemType = 11 -- change here
)
SELECT item, itemType FROM recordList
UNION
SELECT a.item, a.itemType
FROM SampleTable a
INNER JOIN recordList b
ON a.item = b.item
WHERE a.itemtype = 0
SQLFiddle Demo
SQLFiddle Demo (using IN clause for multiple values)

Parent row missing in child parent relationship in with CTE

i have a temporary table in which i have the following data , i want to filter the rows of child with his parent categoryID untill its reaches at the top of that Parent in those hierarchy .
;with cte (rowid,ParentCategoryID,CategoryID,Status,Level,CategoryName,ISProduct) as
(
Select rowid,ParentCategoryID,CategoryID,Status,Level,CategoryName,ISProduct from #newtemp where ParentCategoryId!=0
union all
select cte.rowid,cte.ParentCategoryID,cte.CategoryID,cte.Status,cte.Level,cte.CategoryName,cte.ISProduct
from #newtemp inner join cte ON cte.CategoryId=#newtemp.ParentCategoryId
)
select * from cte
You need replace cte.CategoryId=#newtemp.ParentCategoryId on c.ParentCategoryId = #newtemp.CategoryID
;with cte (rowid,ParentCategoryID,CategoryID,Status,Level,CategoryName,ISProduct) as
(
Select rowid, ParentCategoryID, CategoryID, Status, Level, CategoryName, ISProduct
from #newtemp
where ParentCategoryId!=0
union all
select t.rowid, t.ParentCategoryID, t.CategoryID, t.Status, t.Level, t.CategoryName, t.ISProduct
from #newtemp t inner join cte c ON c.ParentCategoryId = t.CategoryID
)
select * from cte
Demo on SQLFiddle
If I understand you correct. You what something like this:
First some test data:
DECLARE #tbl TABLE
(
rowid INT,
parentCategoryID INT,
CategoryID INT,
[Status] INT,
[Level] INT,
CategoryName VARCHAR(100),
ISProduct BIT
)
INSERT INTO #tbl
SELECT 1,0,1,1,0,'jewellary',1 UNION ALL
SELECT 2,0,2,1,0,'f',0 UNION ALL
SELECT 11,2,4,1,10,'ghdf',1
Then the CTE like this:
;WITH cte_name (rowid,CategoryID,parentCategoryID,HasChildren)
AS
(
SELECT
tbl.rowid,
tbl.CategoryID,
tbl.parentCategoryID,
CASE WHEN EXISTS
(
SELECT
NULL
FROM
#tbl AS tblInner
WHERE
tblInner.parentCategoryID=tbl.CategoryID
)
THEN 1
ELSE 0
END
AS HasChildren
FROM
#tbl AS tbl
WHERE
tbl.parentCategoryID=0
UNION ALL
SELECT
tbl.rowid,
tbl.CategoryID,
tbl.parentCategoryID,
cte.HasChildren
FROM
#tbl AS tbl
JOIN cte_name AS cte
on cte.CategoryID=tbl.parentCategoryID
)
SELECT
tbl.*
FROM
cte_name
JOIN #tbl AS tbl
ON cte_name.rowid=tbl.rowid
WHERE
cte_name.HasChildren=1

t-sql recursive query

Based on an existing table I used CTE recursive query to come up with following data. But failing to apply it a level further.
Data is as below
id name parentid
--------------------------
1 project 0
2 structure 1
3 path_1 2
4 path_2 2
5 path_3 2
6 path_4 3
7 path_5 4
8 path_6 5
I want to recursively form full paths from the above data. Means the recursion will give the following output.
FullPaths
-------------
Project
Project\Structure
Project\Structure\Path_1
Project\Structure\Path_2
Project\Structure\Path_3
Project\Structure\Path_1\path_4
Project\Structure\Path_2\path_5
Project\Structure\Path_3\path_6
Thanks
Here's an example CTE to do that:
declare #t table (id int, name varchar(max), parentid int)
insert into #t select 1, 'project' , 0
union all select 2, 'structure' , 1
union all select 3, 'path_1' , 2
union all select 4, 'path_2' , 2
union all select 5, 'path_3' , 2
union all select 6, 'path_4' , 3
union all select 7, 'path_5' , 4
union all select 8, 'path_6' , 5
; with CteAlias as (
select id, name, parentid
from #t t
where t.parentid = 0
union all
select t.id, parent.name + '\' + t.name, t.parentid
from #t t
inner join CteAlias parent on t.parentid = parent.id
)
select *
from CteAlias
Try something like this:
WITH Recursive AS
(
SELECT
ID,
CAST(PathName AS VARCHAR(500)) AS 'FullPaths',
1 AS 'Level'
FROM
dbo.YourTable
WHERE
ParentID = 0
UNION ALL
SELECT
tbl.ID,
CAST(r.FullPaths + '\' + tbl.PathName AS VARCHAR(500)) AS 'FullPaths',
r.Level + 1 AS 'Level'
FROM
dbo.YourTable tbl
INNER JOIN
Recursive r ON tbl.ParentID = r.ID
)
SELECT * FROM Recursive
ORDER BY Level, ID
Output:
ID FullPaths Level
1 project 1
2 project\structure 2
3 project\structure\path_1 3
4 project\structure\path_2 3
5 project\structure\path_3 3
6 project\structure\path_1\path_4 4
7 project\structure\path_2\path_5 4
8 project\structure\path_3\path_6 4
try this:
DECLARE #YourTable table (id int, nameof varchar(25), parentid int)
INSERT #YourTable VALUES (1,'project',0)
INSERT #YourTable VALUES (2,'structure',1)
INSERT #YourTable VALUES (3,'path_1',2)
INSERT #YourTable VALUES (4,'path_2',2)
INSERT #YourTable VALUES (5,'path_3',2)
INSERT #YourTable VALUES (6,'path_4',3)
INSERT #YourTable VALUES (7,'path_5',4)
INSERT #YourTable VALUES (8,'path_6',5)
;WITH Rec AS
(
SELECT
CONVERT(varchar(max),nameof) as nameof,id
FROM #YourTable
WHERE parentid=0
UNION ALL
SELECT
CONVERT(varchar(max),r.nameof+'\'+y.nameof), y.id
FROM #yourTable y
INNER jOIN Rec r ON y.parentid=r.id
)
select * from rec
output:
nameof
-----------------------------------------------
project
project\structure
project\structure\path_1
project\structure\path_2
project\structure\path_3
project\structure\path_3\path_6
project\structure\path_2\path_5
project\structure\path_1\path_4
(8 row(s) affected)
Something like
;WITH MyCTE AS
(
SELECT
name AS FullPaths, id
FROM
MyTable
WHERE
parentid = 0 /*Normally it'd be IS NULL with an FK linking the 2 columns*/
UNION ALL
SELECT
C.FullPaths + '\' + M.name, M.id
FROM
MyCTE C
JOIN
MyTable M ON M.parentid = C.id
)
SELECT FullPaths FROM MyCTE
You'll have to change the name of #test table I was using.
WITH cte(id, name, parentid) AS
(
SELECT id, convert(varchar(128), name), parentid
FROM #test
WHERE parentid = 0
UNION ALL
SELECT t.id, convert(varchar(128), c.name +'\'+t.name), t.parentid
FROM #test t
INNER JOIN cte c
ON c.id = t.parentid
)
SELECT name as FullPaths
FROM cte
order by id