Recursive CTE to find Total for all children parent and depth - sql

I hope use Recursive CTE to find Total for all childrencount and parentcount and path and level and currentlevel like this
Id ParentId Name Path Level CurrentLevel ChildrenCount ParentCount
1 NULL a 1 4 1 3 0
3 1 c 3,1 3 2 2 1
4 3 d 4,3,1 2 3 1 2
5 4 f 5,4,3,1 1 4 0 3
2 NULL b 2 5 1 5 0
6 2 g 6,2 4 2 4 1
7 6 h 7,6,2 3 3 3 2
8 7 i 8,7.6.2 2 4 2 3
9 8 j 9,8,7,6,2 1 5 0 4
10 8 k 10,8,7,6,2 1 5 0 4
I tried the following code, but I don't know how to get childrencount and parentcount and path and level and currentlevel, how to code it dynamically calculate it.
CREATE TABLE #temp([id] int, [parentid] int null,[name] varchar(5));
INSERT INTO #temp ([id], [parentid], [name])
VALUES ('1', null,'a')
, ('2', null,'b')
, ('3', '1','c')
, ('4', '3','d')
, ('5', '4','e')
, ('6', '2','f')
, ('7', '6','h')
, ('8', '7','i')
, ('9', '8','j')
, ('10', '8','k')
WITH AllChildrens as
(
SELECT p.*, CAST(P.Id AS VarChar(Max)) as [Path]
FROM Department P where p.ParentId is null
UNION ALL
SELECT P1.*, CAST(P1.Id AS VarChar(Max)) + ',' + M.[Path]
FROM Department P1
INNER JOIN AllChildrens M
ON M.Id = P1.ParentId
)
SELECT Id,ParentId,Name,Path From AllChildrens order by Id

One option is to use the data type hierarchyid
Example
;with cteP as (
Select ID
,parentid
,Name
,HierID = convert(hierarchyid,concat('/',ID,'/'))
From #Temp
Where parentid is null
Union All
Select ID = r.ID
,parentid = r.parentid
,Name = r.Name
,HierID = convert(hierarchyid,concat(p.HierID.ToString(),r.ID,'/'))
From #Temp r
Join cteP p on r.parentid = p.ID)
Select ID
,parentid
,Name
,Path = HierID.ToString()
,Depth = ( Select max(HierID.GetLevel() ) from cteP where HierID.ToString() like A.HierID.ToString()+'%') - HierID.GetLevel()
,Lvl = HierID.GetLevel()
,ChildCnt = ( Select count(*) from cteP where HierID.ToString() like A.HierID.ToString()+'%') -1
,ParentCnt = len(HierID.ToString()) - len(replace(HierID.ToString(),'/','')) - 2
From cteP A
Order By A.HierID
Results

Related

Recursive query with parent-child relation

I am trying to make a recursive query in SQL Server, that display data hierarchically. Here is the structure of the table
[id] [int] IDENTITY(1,1) NOT NULL,
[name] [varchar(100)] NOT NULL,
[Parent_Id] [int] NULL,
Each product has a parent. The column Parent_Id content the id of the parent. The parent_id is null for root products.
I want to make a sql query that display products hierarchically. The following image is an example of how the products could be organized.
Products can have products childs.
For the picture above, the query result should be like the following :
id name parent_id
1 P1 NULL
2 P2 NULL
3 P2-1 2
4 P2-2 2
5 P2-3 2
6 P2-3-1 5
7 P2-3-2 5
8 P3 NULL
9 P3-1 8
Here is the request I wrote to achieve it :
with tree as (select * from products
union all
select * from tree where parent_id = tree.id
)
select * from tree;
But I get a result similar to the following:
1 P1 NULL
2 P2 NULL
8 P3 NULL
3 P2-1 2
4 P2-2 2
5 P2-3 2
9 P3-1 8
6 P2-3-1 5
7 P2-3-2 5
What I want is to group each products sibling so that each product is displayed under its direct parent.
Just another option using the data type hierarchyid
There are some additional features and functions associated with hierarchyid
Example
-- Optional See 1st WHERE
Declare #Top int = null --<< Sets top of Hier Try 2
;with cteP as (
Select ID
,parent_id
,Name
,HierID = convert(hierarchyid,concat('/',ID,'/'))
From YourTable
Where IsNull(#Top,-1) = case when #Top is null then isnull(parent_id ,-1) else ID end
--Where parent_id is null -- Use this where if you always want the full hierarchy
Union All
Select ID = r.ID
,parent_id = r.parent_id
,Name = r.Name
,HierID = convert(hierarchyid,concat(p.HierID.ToString(),r.ID,'/'))
From YourTable r
Join cteP p on r.parent_id = p.ID)
Select Lvl = HierID.GetLevel()
,ID
,parent_id
,Name
From cteP A
Order By A.HierID
Results
Lvl ID parent_id Name
1 1 NULL P1
1 2 NULL P2
2 3 2 P2-1
2 4 2 P2-2
2 5 2 P2-3
3 6 5 P2-3-1
3 7 5 P2-3-2
1 8 NULL P3
2 9 8 P3-1
Just for fun, If I set #Top to 2, the results would be
Lvl ID parent_id Name
1 2 NULL P2
2 3 2 P2-1
2 4 2 P2-2
2 5 2 P2-3
3 6 5 P2-3-1
3 7 5 P2-3-2
Construct the path in the recursive query. The following does this as a string with fixed length ids:
with tree as (
select p.id, p.name, p.parentid,
format(p.parentid, '0000') as path
from products p
where p.parentid is null
union all
select p.id, p.name, p.parentid,
concat(cte.path, '->', format(p.id, '0000')
from tree join
products p
where p.parent_id = t.id
)
select *
from tree;
If I understand this correct and you have the result you want but just unordered, you should be able to just order the result by name.
with tree as (select * from products
union all
select * from tree where parent_id = tree.id
)
select * from tree order by name asc;

Recursive CTE - recalculate the tree after exclusions

Lets say I have a table called #OrgList
CREATE TABLE #OrgList (
OrgUnitId int,
ParentOrgUnitId int,
PersonId int,
isExcluded bit
);
INSERT INTO #OrgList(OrgUnitId, ParentOrgUnitId, PersonId, isExcluded) VALUES
(1, NULL, 100, 0), (2,1, 101, 0), (3,1,102,0), (4,2,103,1), (5,2,104,0), (6,3,105,0), (7,4,106,0), (8,4,107,0), (9,4,108,0), (10,4,109,0), (11,4,110,1), (12,11,111,0)
My hierarchy tree structure looks like this
and my cte like this:
;
with cte as (
select OrgUnitId, ParentOrgUnitId, PersonId, isExcluded , 0 as level_num
from #OrgList
where ParentOrgUnitId is null
UNION ALL
select o.OrgUnitId, o.ParentOrgUnitId, o.PersonId, o.isExcluded , level_num+1 as level_num
from #OrgList o
join cte on o.ParentOrgUnitId=cte.OrgUnitId
)
select * from cte
I exclude OrgUnitId=4 and =11, then I want to update my recursive query that will recalculate the tree and show the new tree details, including level moves (there can be more levels and more consecutive exclusions, of course except the root node):
You should just add a second UNION ALL in your cte:
with cte as (
select OrgUnitId, ParentOrgUnitId, PersonId, isExcluded , 0 as level_num
from #OrgList
where ParentOrgUnitId is null
UNION ALL
select o.OrgUnitId, o.ParentOrgUnitId, o.PersonId, o.isExcluded , level_num+1 as level_num
from #OrgList o
join cte on o.ParentOrgUnitId=cte.OrgUnitId
where cte.isExcluded = 0
UNION ALL
select o.OrgUnitId, cte.ParentOrgUnitId, o.PersonId, o.isExcluded , level_num as level_num
from #OrgList o
join cte on o.ParentOrgUnitId=cte.OrgUnitId
where cte.isExcluded = 1
)
select * from cte
My approach:
Extend your initial CTE with an exclusion counter (ExclusionCount), counting the number of excluded nodes going from root to leaf nodes.
Add another recursive CTE to construct the upward path (cte_upwards) for each leaf node. Now decrement the counter added in the initial CTE.
Use a cross apply to select the first node where the upward path reaches an exlusion count of zero.
Solution:
with cte as -- initial CTE
(
select OrgUnitId,
ParentOrgUnitId,
PersonId,
IsExcluded,
convert(int, IsExcluded) as 'ExclusionCount', -- new counter
0 as 'level_num'
from #OrgList
where ParentOrgUnitId is null
union all
select o.OrgUnitId,
o.ParentOrgUnitId,
o.PersonId,
o.IsExcluded,
cte.ExclusionCount + convert(int, o.isExcluded), -- increment counter
cte.level_num + 1
from #OrgList o
join cte on o.ParentOrgUnitId = cte.OrgUnitId
),
cte_upwards as
(
select cte.OrgUnitId,
cte.ParentOrgUnitId as 'NewParentOrgUnitId',
cte.IsExcluded,
cte.ExclusionCount,
cte.level_num
from cte
where cte.ParentOrgUnitId is not null -- only leaf nodes (not a root)
and not exists ( select top 1 'x' -- only leaf nodes (not an intermediate node)
from cte cp
where cp.ParentOrgUnitId = cte.OrgUnitId )
union all
select cte_upwards.OrgUnitId,
cte.ParentOrgUnitId,
cte.IsExcluded,
cte_upwards.ExclusionCount - cte.IsExcluded, -- decrement counter
cte.level_num
from cte_upwards
join cte
on cte.OrgUnitId = cte_upwards.NewParentOrgUnitId
)
select cte.OrgUnitId,
cte.ParentOrgUnitId,
cte.IsExcluded,
x.NewParentOrgUnitId,
coalesce(x.NewParentOrgUnitId, cte.ParentOrgUnitId) as 'Recalculated'
from cte
outer apply ( select top 1 cu.NewParentOrgUnitId
from cte_upwards cu
where cu.OrgUnitId = cte.OrgUnitId
and cu.ExclusionCount = 0 -- node without excluded parent nodes
order by cu.level_num desc ) x -- select lowest node in upwards path
order by cte.OrgUnitId;
Result:
OrgUnitId ParentOrgUnitId IsExcluded NewParentOrgUnitId Recalculated
----------- --------------- ---------- ------------------ ------------
1 NULL 0 NULL NULL
2 1 0 NULL 1
3 1 0 NULL 1
4 2 1 NULL 2
5 2 0 2 2
6 3 0 3 3
7 4 0 2 2
8 4 0 2 2
9 4 0 2 2
10 4 0 2 2
11 4 1 NULL 4
12 11 0 2 2
I've added a VirtualParentOrgUnitId, which contains the parent with excluded nodes taken into account. I've also added a counter, VirtualDistance, which will report how many real hops there are between this node and it's virtual parent.
VirtualParentOrgUnitId will use the parent's ID if is not excluded, otherwise it will use it's parents's VirtualParentOrgUnitId, which allows chaining of multiple levels.
DROP TABLE IF EXISTS #OrgList
CREATE TABLE #OrgList (
OrgUnitId int,
ParentOrgUnitId int,
PersonId int,
isExcluded bit
);
INSERT INTO #OrgList(OrgUnitId, ParentOrgUnitId, PersonId, isExcluded) VALUES
(1, NULL, 100, 0), (2,1, 101, 0), (3,1,102,0), (4,2,103,1), (5,2,104,0), (6,3,105,0), (7,4,106,0), (8,4,107,0), (9,4,108,0), (10,4,109,0), (11,4,110,1), (12,11,111,0)
DROP TABLE IF EXISTS #Excludes
CREATE Table #Excludes (
OrgUnitId int
);
INSERT INTO #Excludes VALUES (4), (11);
with cte as (
select OrgUnitId, ParentOrgUnitId, ParentOrgUnitId VirtualParentOrgUnitId, 1 as VirtualDistance , PersonId, isExcluded , 0 as level_num
from #OrgList
where ParentOrgUnitId is null
UNION ALL
select o.OrgUnitId, o.ParentOrgUnitId, IIF(o.ParentOrgUnitId IN (SELECT OrgUnitId FROM #Excludes),cte.VirtualParentOrgUnitId, o.ParentOrgUnitId ), IIF(o.ParentOrgUnitId IN (SELECT OrgUnitId FROM #Excludes),VirtualDistance + 1, 1 ), o.PersonId, o.isExcluded , level_num+1 as level_num
from #OrgList o
join cte on o.ParentOrgUnitId=cte.OrgUnitId
)
select * from cte
Here are the results:
OrgUnitId ParentOrgUnitId VirtualParentOrgUnitId VirtualDistance PersonId isExcluded level_num
----------- --------------- ---------------------- --------------- ----------- ---------- -----------
1 NULL NULL 0 100 0 0
2 1 1 0 101 0 1
3 1 1 0 102 0 1
6 3 3 0 105 0 2
4 2 2 0 103 1 2
5 2 2 0 104 0 2
7 4 2 1 106 0 3
8 4 2 1 107 0 3
9 4 2 1 108 0 3
10 4 2 1 109 0 3
11 4 2 1 110 1 3
12 11 2 2 111 0 4
;
with cte as (
select OrgUnitId, ParentOrgUnitId, PersonId, isExcluded , 0 as level_num, 0 as level_after_exclusions,
cast(',' as varchar(max)) + case isExcluded when 1 then cast(OrgUnitId as varchar(20)) else '' end as excludedmembers,
case isExcluded when 1 then ParentOrgUnitId end as newParentId
from #OrgList
where ParentOrgUnitId is null
UNION ALL
select o.OrgUnitId, o.ParentOrgUnitId, o.PersonId, o.isExcluded , level_num + 1, level_after_exclusions + case o.isExcluded when 1 then 0 else 1 end,
excludedmembers + case o.isExcluded when 1 then cast(o.OrgUnitId as varchar(20))+',' else '' end,
case when excludedmembers like '%,'+cast(o.ParentOrgUnitId as varchar(20))+',%' then newParentId else o.ParentOrgUnitId end
from #OrgList o
join cte on o.ParentOrgUnitId=cte.OrgUnitId
)
select *, level_num - level_after_exclusions as shiftbylevels
from cte

SQL: Get Parent Tree Hierarchy from child

I have a table with Id, ParentId, Tree, TopParentId. Here's an example structure:
0
___/ \___
/ \
1 4
/ \ / \
2 7 5 8
/ /
3 6
Input Table:
Id ParentId Tree TopParentId
--- ---------- -------------------- --------
1 0 NULL NULL
2 1 NULL NULL
7 1 NULL NULL
3 2 NULL NULL
4 0 NULL NULL
5 4 NULL NULL
6 5 NULL NULL
8 4 NULL NULL
This is the output I am looking for when I pass Id = 3
OUTPUT:
Id ParentId Tree TopParentId
--- ---------- -------------------- --------
1 2 3 > 2 > 1 > 0 0
The CTE query should be able to handle multiple ids like 3,7
OUTPUT:
Id ParentId Tree TopParentId
--- ---------- -------------------- --------
3 2 3 > 2 > 1 > 0 0
7 1 7 > 1 > 0 0
The end goal is to take the Tree and TopParentId columns and update the corresponding Id
Here's the query I have tried till now:
WITH CTE AS (
SELECT Id, ParentId,0 AS [Level], CAST(Id AS varchar(1000)) AS Heirarchy,Id AS TopParentId
FROM dbo.table
WHERE Id IN (SELECT Id FROM table WHERE ParentId IS NULL)
UNION ALL
SELECT mgr.Id, mgr.ParentId, TASKCTE.[Level] +1 AS [Level],
CAST(( CAST(mgr.Id AS VARCHAR(1000)) + '>' + CTE.Heirarchy) AS varchar(1000)) AS Heirarchy, CTE.TopParentId
FROM CTE
INNER JOIN dbo.table AS mgr
ON CTE.Id = mgr.ParentId
)
UPDATE t SET t.[LEVEL] = TC.[LEVEL], t.ParentTree = TC.Heirarchy, t.TopParentId = TC.TopParentId
FROM dbo.table AS t
JOIN (SELECT * FROM CTE WHERE Id IN(SELECT DISTINCT Id FROM INSERTED) AND ParentId IS NOT NULL) TC
ON
t.Id = TC.Id
The above query works but its CPU/RAM intensive as it starts from Parent. I need the CTE to start from the Child but the Tree needs to be exactly same as the example outputs.
This appears to work:
drop table if exists #t;
with cte as (
select * from (values
(1, 0),
(2, 1),
(7, 1),
(3, 2),
(4, 0),
(5, 4),
(6, 5),
(8, 4)
)
as x(ID, ParentID)
)
select *
into #t
from cte;
with cte as (
select * ,
ID as [start] ,
ParentID as [FirstParent] ,
1 as [level] ,
cast(ID as varchar(max)) as Tree
from #t
where ID in (3, 7)
union all
select p.*,
c.[start] ,
c.FirstParent ,
c.[level] + 1 ,
cast(concat(c.Tree, ' > ', p.ID) as varchar(max))
from cte as c
join #t as p
on c.ParentID = p.ID
), top_level as (
select *, row_number() over (partition by [start] order by [level] desc) as rn
from cte
)
select [start] as ID ,
FirstParent as ParentID ,
concat(Tree, ' > ', ParentID) ,
ParentID as TopParentID
from top_level
where rn = 1;
By way of exposition, the first part just creates your test data (pro-tip: if you do this, people are more likely to be able to help since you've lowered the friction to do so!). The meat of the solution just uses the desired IDs as the "base case" for the recursion and the recursive step says "take the previous level's ParentID to find the next level's ID". The rest is just to keep track of the starting and ending point.
You simply need to create procedure to get the desired result.
Try this or variant of this will help.
Create proc CalculateLevel
( #passid nvarchar(max) )
As
Begin
declare #query nvarchar(max)
set #query = '
WITH CTE AS (
SELECT Id, ParentId,0 AS [Level], CAST(Id AS varchar(1000)) AS Heirarchy,Id AS TopParentId
FROM dbo.tab
WHERE Id IN (SELECT Id FROM tab WHERE ParentId IS NULL)
UNION ALL
SELECT mgr.Id, mgr.ParentId, CTE.[Level] +1 AS [Level],
CAST(( CAST(mgr.Id AS VARCHAR(1000)) + ''>'' + CTE.Heirarchy) AS varchar(1000)) AS Heirarchy, CTE.TopParentId
FROM CTE
INNER JOIN dbo.tab AS mgr
ON CTE.Id = mgr.ParentId
)
UPDATE t SET t.[LEVEL] = TC.[LEVEL], t.ParentTree = TC.Heirarchy, t.TopParentId = TC.TopParentId
FROM dbo.tab AS t
JOIN (SELECT * FROM CTE WHERE ParentId IS NOT NULL) TC
ON
t.Id = TC.Id where t.id in ( ' + #passid + ')'
print #query
exec sp_executesql #query
End

Group result after count() as single line for each group in SQL Server

I have these 3 tables mar_tb, sel_tb, cust_tb.
mar_tb
mar_id (int) PK
mar_name (nvarchar(50)) not null
sel_tb
sel_id (int) pk
mar_id (int) (not null) FK
cust_tb
cust_id (int) pk
cust_active (bit) not null)
mar_id (int) (not null) FK
I have this data in each table
mar_tb
mar_id | mar_name
-----------------
1 mar_one
2 mar_two
3 mar_three
sel_tb
sel_id | mar_id
----------------------------
1 1
2 1
cust_tb
cust_id | cust_active | mar_id
----------------------------
1 1 1
2 1 1
3 1 1
4 1 1
5 1 1
6 1 1
7 1 1
8 1 1
9 1 1
10 1 1
11 1 1
12 1 1
13 1 2
14 1 2
15 1 2
16 1 2
All I need to get result like this
mar_name | cus_num | sel_num
--------------------------------
mar_one 12 2
mar_three 0 0
mar_two 4 0
I tried to write simple code like this
select
mar_tb.mar_name,
count(cust_tb.cust_id) as 'cus_num',
count(sel_tb.sel_id) over (PARTITION by mar_tb.mar_name ) as 'sel_num'
from
mar_tb
left join
cust_tb on cust_tb.mar_id = mar_tb.mar_id
left join
sel_tb on sel_tb.mar_id = mar_tb.mar_id
group by
mar_tb.mar_name, sel_tb.sel_id
and I got this result
mar_name | cus_num | sel_num
--------------------------------
mar_one 12 2
mar_one 12 2
mar_three 0 0
mar_two 4 0
Then I solved this issue by using subquery like this
select
mar_name, cus_num, sel_num
from
(select
mar_tb.mar_name,
count(cust_tb.cust_id) as 'cus_num',
count(sel_tb.sel_id)over (PARTITION by mar_tb.mar_name ) as 'sel_num'
from
mar_tb
left join
cust_tb on cust_tb.mar_id = mar_tb.mar_id
left join
sel_tb on sel_tb.mar_id = mar_tb.mar_id
group by
mar_tb.mar_name, sel_tb.sel_id) a
group by
mar_name, sel_num, cus_num
Finally I got what i need
mar_name | cus_num | sel_num
--------------------------------
mar_one 12 2
mar_three 0 0
mar_two 4 0
The question is: is there any way to get what I need without using subquery or (distinct) clause?
If I understand correctly, I would use union all and group by:
select m.mar_name, sum(cus_num) as cus_num, sum(sel_num) as sel_num
from mar_tb m left join
((select c.mar_id, 1 as cus_num, 0 as sel_num
from cust_tb c
) union all
(select s.mar_id, 0, 1
from sel_tb s
)
) cs
on m.mar_id = cs.mar_id
group by m.mar_name;
An alternative approach uses apply or correlated subqueries:
select m.mar_name, c.cus_num, s.sel_num
from mar_tb m outer apply
(select count(*) as cus_num
from cust_tb c
where c.mar_id = m.mar_id
) c outer apply
(select count(*) as sel_num
from sel_tb s
where s.mar_id = m.mar_id
) s;
Both of these use subqueries, but they are much more direct calculations of the values you want. I strongly encourage you to be more concerned about using distinct than about using subqueries.
If I understand your question correctly then you can try 2 ways as well shown below.
DECLARE #Mar_tb AS TABLE (mar_Id INT, mar_Name VARCHAR(100))
DECLARE #Sel_tb AS TABLE (sel_Id INT,Mar_Id INT)
DECLARE #cust_tb AS TABLE
(
cust_id int,
cust_active bit,
mar_id int)
INSERT INTO #mar_tb (mar_id ,mar_name)
VALUES
(1,'mar_one'),
(2,'mar_two'),
(3,'mar_three')
INSERT INTO #sel_tb(
sel_id ,mar_id)
VALUES(1 , 1),
(2 , 1),
(3 , 2)
INSERT INTO #cust_tb
(cust_id , cust_active , mar_id)
VALUES
( 1 , 1 , 1),
( 2 , 1 , 1),
( 3 , 1 , 1),
( 4 , 1 , 1),
( 5 , 1 , 1),
( 6 , 1 , 1),
( 7 , 1 , 1),
( 8 , 1 , 1),
( 9 , 1 , 1),
(10 , 1 , 1),
(11 , 1 , 1),
(12 , 1 , 1),
(13 , 1 , 2),
(14 , 1 , 2),
(15 , 1 , 2),
(16 , 1 , 2)
/*
mar_name | cus_num | sel_num
--------------------------------
mar_one 12 2
mar_three 0 0
mar_two 4 0 */
/************** Approach 1****/
SELECT m.mar_name,COUNT(DISTINCT s.sel_id) as sal, Count(DISTINCT c.cust_id) as customer
FROM #mar_tb m
LEFT OUTER JOIN #sel_tb s ON s.mar_id = m.mar_id
LEFT OUTER JOIN #cust_tb c ON c.mar_id = m.mar_id
GROUP BY m.mar_name
/************** Approach 2****/
SELECT m.mar_Name,COUNT(c.cust_Id) AS CustCount, tmp.saleCount
FROM #mar_tb m
LEFT OUTER JOIN #cust_tb c ON c.mar_id = m.mar_id
CROSS APPLY (SELECT COUNT(1) As saleCount
FROM #Sel_tb s
WHERE s.Mar_Id = m.mar_Id )tmp
GROUP BY m.mar_name,tmp.saleCount

SQL Query Parent Child

Can you help me?
Table: LOS
ID Name ParentID
1 Item Null
2 Pharmacy 1
3 Consumable 1
4 Ethical 2
5 Non Ethical 2
6 MCCP 4
7 Nurse 3
Table: Item
id ItemName lOS_Id Los_Name
123 Panadol 6 MCCP
321 Nacl 7 Nurse
Expected Result.
ID ItemName ParentID ParentName LOS_Id Los_Name
123 Panadol 2 Pharmacy 6 MCCP
321 Nacl 3 Consumable 7 Nurse
Explanation : ParentID from los_id join LOS Table using ID THEN Get parentID to find Parent
Just see this example.
Declare #LOS table (ID int, Name nvarchar(50), ParentID int)
Declare #Item table (ID int, ItemName nvarchar(50), lOS_ID int, lOS_Name nvarchar(50) )
Insert into #LOS values (1, 'Item',null), (2 ,'Pharmacy', 1), (3 ,'Consumable', 1), (4 ,'Ethical', 2), (5 ,'Non Ethical', 2), (6 ,'MCCP', 4)
, (7 ,'Nurse', 3)
Insert into #Item values (123, 'Panadol',6 ,'MCCP'), (321, 'Nacl',7 ,'Nurse')
select * from #LOS
select * from #Item
;with cte as
(
select i.id, ItemName, lOS_ID, lOS_Name, lOS_ID parentid, lOS_ID parentparentid, lOS_Name parentname from #Item i
join #los l on i.lOS_ID = l.id
-- where i.id = 123
union all
select
c.id, ItemName, lOS_ID, lOS_Name, l.ID parentid, l.ParentID parentparentid, l.Name parentname
from
cte c join #los l on c.parentparentid = l.id --and c.ParentID <> null
)
select * from cte where parentparentid = 1
order by id
I hope I understood you correctly and this query will fix your problem :)
select I.ID, I.ItemName, A.ParentID, A.ParentName, I.LOS_ID, I.LOS_Name from Item AS I join (
select l1.id, l1.name, l1.ParentID, l2.name as ParentName from LOS as l1 join LOS as l2 on l1.id = l2.ParentID
) as A join on I.LOS_ID = A.ID