Order of Recursion (SQL Server CTE) - sql-server

I can achieve recursion by using SQL Server's With command (CTE).
WITH MyCTE(ParentID,ID,Name,Level)
AS
(
SELECT ManagerID AS ParentID, UserID AS ID, UserName AS Name, 0 AS Level
FROM USERS U
WHERE U.ManagerID IS NULL
UNION ALL
SELECT U.ManagerID AS ParentID, U.UserID AS ID, U.UserName AS Name, H.Level+1 AS Level
FROM USERS U
INNER JOIN MyCTE H ON H.ID = U.ManagerID
)
SELECT ParentID,ID FROM MyCTE
returns
ParentID ID
NULL 1
1 2
1 3
2 4
What I want to achieve is to reverse this result set. Namely,reversing the root node and the deepest child node as,
ParentID ID
NULL 4
4 2
2 1
3 1
Couldn't figure out how to programmatically implement this (preferably by using CTE), like by using a parameter to determine the recursion order etc. Any help is greatly appreciated, thanks.
Edit :
Modified this a bit inserting my first CTE's results into a temp table, then using another recursion I reverse the order as (I know "WHERE T.ID = (SELECT MAX(ID) FROM #tmp)" wont work in a real situation, I also gotta determine the deepest node with the "Level" column, just tried to simplify this for this example),
INSERT INTO #tmp
SELECT ParentID,ID,Level FROM MyCTE
WITH MyCTE2(ParentID,ID,Level)
AS
(
SELECT NULL AS ParentID, ID AS ID, 0 AS Level FROM #tmp T
WHERE T.ID = (SELECT MAX(ID) FROM #tmp)
UNION ALL
SELECT R2.ID AS ParentID, T.ParentID AS ID, R2.Level+1 FROM #tmp T
INNER JOIN MyCTE2 R2 ON R2.ID = T.ID
WHERE T.ParentID IS NOT NULL
)
Original Results (removed the 1,3 pair)
ParentID ID Level
NULL 1 0
1 2 1
2 4 2
Reversed results,
ParentID ID Level
NULL 4 0
4 2 1
2 1 2
Edit 2:
I did something like this,
SELECT TTT.ParentID,TTT.ID,TTT.Level FROM
(
SELECT ParentID,ID,Level FROM MyCTE2
UNION ALL
SELECT TT.ID AS ParentID,TT.ParentID AS ID,(SELECT Level+1 FROM #tmp WHERE ID=TT.ID)
AS Level FROM
(
SELECT ID FROM #tmp
EXCEPT
SELECT ID FROM MyCTE2
)T INNER JOIN #tmp TT ON TT.ID = T.ID
)TTT
ORDER BY TTT.Level
gives,
ParentID ID Level
NULL 4 0
4 2 1
2 1 2
3 1 2
This may contain errors, im not sure yet, just wanted to show to make sure that pair (3,1) is whther correct with level 2 ? Been thinking on this for quite a while now, I might make some silly mistakes.

Sample data
declare #T table
(
ParentID int,
ID int
)
insert into #T values
(NULL, 1),
(1 , 2),
(1 , 3),
(2 , 4)
Recursion from root:
;with C as
(
select ParentID, ID
from #T
where ParentID is null
union all
select T.ParentID, T.ID
from #T as T
inner join C
on T.ParentID = C.ID
)
select *
from C
Result
ParentID ID
----------- -----------
NULL 1
1 2
1 3
2 4
Recursion from leafs:
;with C as
(
select null as PParentID, ID, ParentID
from #T
where ID not in (select ParentID
from #T
where ParentID is not null)
union all
select C.ID, T.ID, T.ParentID
from #T as T
inner join C
on T.ID = C.ParentID
)
select distinct
PParentID as ParentID,
ID
from C
Result:
ParentID ID
----------- -----------
NULL 3
NULL 4
4 2
2 1
3 1
If you have many branches you will have duplicate rows as merge together. Using distinct takes care of that.
To get the levels correct you need to first calculate the level from top down. Store that in a table variable (or temp table) and then use that as the source for leaf->root recursion.
-- Primary key and unique is in there to get the indexes used in the recursion
declare #T2 table
(
ParentID int,
ID int,
Level int,
primary key (ID),
unique(ParentID, ID)
)
;with C as
(
select ParentID, ID, 0 as Level
from #T
where ParentID is null
union all
select T.ParentID, T.ID, Level + 1
from #T as T
inner join C
on T.ParentID = C.ID
)
insert into #T2
select ParentID, ID, Level
from C
;with C as
(
select null as PParentID, ID, ParentID, Level
from #T2
where ID not in (select ParentID
from #T2
where ParentID is not null)
union all
select C.ID, T.ID, T.ParentID, T.Level
from #T2 as T
inner join C
on T.ID = C.ParentID
)
select distinct
PParentID as ParentID,
ID,
max(Level) over() - Level as level
from C
Result:
ParentID ID level
----------- ----------- -----------
NULL 3 1
NULL 4 0
2 1 2
3 1 2
4 2 1
It is possible but a really bad idea to replace #T2 with a multi CTE query. It will kill performance because to first CTE will be rebuilt for each recursion. At least that is my guess of what is happening but believe me it is not fast.

Related

T-SQL - Next row with greater value, continuously

I have table described bellow from which I need to select all rows with [Value] greater for example at least 5 points than [Value] from previous row (ordered by [Id]). Starting with first row of [Id] 1, desired output would be:
[Id] [Value]
---------------
1 1
4 12
8 21
Code:
declare #Data table
(
[Id] int not null identity(1, 1) primary key,
[Value] int not null
);
insert into #Data ([Value])
select 1 [Value]
union all
select 5
union all
select 3
union all
select 12
union all
select 8
union all
select 9
union all
select 16
union all
select 21;
select [t1].*
from #Data [t1];
Edit:
So, based on JNevill's and Hogan's answers I end with this:
;with [cte1]
as (
select [t1].[Id],
[t1].[Value],
cast(1 as int) [rank]
from #Data [t1]
where [t1].[Id] = 1
union all
select [t2].[Id],
[t2].[Value],
cast(row_number() over (order by [t2].id) as int) [rank]
FROM [cte1] [t1]
inner join #Data [t2] on [t2].[value] - [t1].[value] > 5
and [t2].[Id] > [t1].[Id]
where [t1].[rank] = 1
)
select [t1].[Id],
[t1].[Value]
from [cte1] [t1]
where [t1].[rank] = 1;
which is working. Alan Burstein answer is correct too (but applicable only on MSSQL 2012+ - due to LAG fc). I will do some performance tests (I'm on 2016 version) and will see performance over my real data (approx. 30 millions of records).
If you are on 2012+ you can use LAG which will provide a better performing solution that a recursive CTE. I'm including your sample data so you can just copy/paste/test...
-- Your sample data
DECLARE #Data TABLE
(
Id int not null identity(1, 1) primary key,
Value int not null
);
insert into #Data ([Value])
select 1 [Value] union all select 5 union all select 3 union all select 12 union all
select 8 union all select 9 union all select 16 union all select 21;
-- Solution using window functions
WITH
prevRows AS
(
SELECT t1.Id, t1.Value, prevDiff = LAG(t1.Value, 1) OVER (ORDER BY t1.id) - t1.Value
FROM #Data t1
),
NewPrev AS
(
SELECT t1.Id, t1.Value, NewDiff = Value - LAG(t1.Value,1) OVER (ORDER BY t1.id)
FROM prevRows t1
WHERE prevDiff <= -5 OR prevDiff IS NULL
)
SELECT t1.Id, t1.Value
FROM NewPrev t1
WHERE NewDiff >= 5 OR NewDiff IS NULL;
I believe the best way to pull this off is using a recursive CTE. A Recursive CTE is a special type of CTE that refers back to itself. It's made up of two parts.
The recursive seed/anchor which establishes the beginning of the recursion. In your case, record with ID=1.
The recursive term/member which is the statement that refers back to itself by the name of the CTE. Here we pull through the next record that is greater than 5 from the previous found record according to the ID sorted ascending.
Code:
WITH RECURSIVE recCTE AS
(
/*Select first record for recursive seed/anchor*/
SELECT
id,
value,
cast(1 as INT) as [rank]
FROM table
WHERE id = 1
UNION ALL
/*find the next value that is more than 5 from the current value*/
SELECT
table.id,
table.value
ROW_NUMBER() OVER (ORDER BY id)
FROM
recCTE INNER JOIN table
ON table.value - recCTE.value > 5
AND table.id > recCTE.id
WHERE recCTE.[rank]=1
)
SELECT id, value FROM recCTE;
I've made use of the Row_Number() Window Function to find the rank of the matching record by ID sorted Ascending. With the WHERE clause in the recursive term we only grab the first found record that is 5 more than the previous found record. Then we head into the next recursive step.
You can do it with a recursive CTE
with find_values as
(
-- Find first value
SELECT Value
FROM #Table
ORDER BY ID ASC
FETCH FIRST 1 ROW ONLY
UNION ALL
-- Find next value
SELECT Value
FROM #Table
CROSS JOIN find_values
WHERE Value >= find_values.Value + 5
ORDER BY ID ASC
FETCH FIRST 1 ROW ONLY
)
SELECT *
FROM find_values

Rewriting function to conditional CTE

Considering following table:
SELECT [ItemID]
,[ParentID]
,[PolicyID]
,[PolicyRoot]
FROM [AdventureWorks2008R2].[dbo].[Example]
ItemID ParentID PolicyID PolicyRoot
----------- ----------- ---------- ----------
1 NULL default 1
2 1 b 1
3 1 c 0
4 NULL d 1
5 3 e 0
6 3 f 1
7 NULL g 0
I'm trying to select the PolicyID from each item where PolicyRoot = 1, in case PolicyRoot = 0 I need to use PolicyID from its ParentID. This is recursive...
Working with a function:
CREATE FUNCTION dbo.Policies(#ItemID INT) RETURNS VARCHAR(10)
AS
BEGIN
DECLARE #ParentID INT, #PolicyRoot BIT, #PolicyID VARCHAR(10)
SELECT #ParentID = ParentID
, #PolicyRoot = PolicyRoot
, #PolicyID = PolicyID
FROM [dbo].[Example]
WHERE ItemID = #ItemID
IF #PolicyRoot != 1
SELECT #PolicyID = dbo.Policies(#ParentID)
RETURN #PolicyID
END;
GO
SELECT ItemID
, dbo.Policies(ItemID) AS Policy
FROM [dbo].[Example];
ItemID Policy
----------- ----------
1 default
2 b
3 default
4 d
5 default
6 f
7 NULL
I'm trying to rewrite this function to a CTE, but I don't have any CTE knowledge yet. I've read into multiple CTE's but I don't have a single clue how to manage a conditional CTE. This is as far as I've gotten, I'm not familiar (enough) with the UNION ALL.
WITH Policies (ItemID, PolicyID) AS (
SELECT ItemID
, PolicyID
FROM dbo.Example
UNION ALL
...
)
SELECT ItemID
, PolicyID
FROM Policies;
Can someone explain me in plain steps how such a CTE works and push me in the right direction?
A recursive CTE works by joining to itself, using a UNION ALL to collate the results.
You start with yourtable to populate the initial dataset of the recursive query
select * from yourtable
and you add to that with the UNION ALL, further results
select c.ItemID, t2.ParentID, t2.PolicyID, t2.PolicyRoot
from yourtable t2
inner join c on c.ParentID = t2.ItemID
where c.PolicyRoot=0
and the recursion occurs in this - where the results of this query are fed through this query again and again, up to the MAXRECURSION limit, or when no more results are added.
;with c as
(
select * from yourtable
union all
select c.ItemID, t2.ParentID, t2.PolicyID, t2.PolicyRoot
from yourtable t2
inner join c on c.ParentID = t2.ItemID
where c.PolicyRoot=0
)
select t.ItemID, c.PolicyID
from yourtable t
left join c on t.ItemID = c.ItemID
and c.PolicyRoot=1

Is there a way to duplicate part of a adjacent list hierarchy given a starting id?

I have an adjacent list hierarchy model that makes up a topic structure
ID Parent_Id Topic_Name
1 Null Topic 1
2 Null Topic 2
3 2 Topic 3
4 3 Topic 4
5 2 Topic 5
6 Null Topic 6
This forms part of an application which I cant change - the topics dont have multiple parents so unfortunatly I can't move to a nested sets - although if this was an interim step in the process - this would be fine as long as it went back to adjacent list hierarchy model
I want to specify a topic id and then copy it to a new topic id and retain the levels / structure underneath
So in my example I could specify topic topic_id 2 and it would create
ID Parent_Id Topic_Name
7 Null Topic 2
8 7 Topic 3
9 8 Topic 4
10 7 Topic 5
Auto numbering is taken care of for the ID so no need to construct that, but obviously the parent id needs to be retained
How can I achieve the above? would I need to flatten the data and do 3 seperate inserts logging the id after each insert?
You can use a recursive CTE to get the rows to insert.
If you use merge to add the rows you can use output to capture a mapping between the generated ID and the old ID which can be used to update the column Parent_ID for the inserted rows.
-- ID for topic to copy
declare #ID int;
set #ID = 2;
-- Table to hold the inserted rows
declare #T table
(
New_ID int,
Old_ID int,
Old_ParentID int
);
-- Add rows from recursive CTE using merge
with C as
(
select T.ID, T.Parent_Id, T.Topic_Name
from YourTable as T
where T.ID = #ID
union all
select T.ID, T.Parent_Id, T.Topic_Name
from YourTable as T
inner join C
on C.ID = T.Parent_Id
)
merge YourTable
using C
on 0 = 1
when not matched then
insert (Topic_Name) values (C.Topic_Name)
output inserted.ID,
C.ID,
C.Parent_Id
into #T(New_ID, Old_ID, Old_ParentID);
-- Update Parent_Id for the new rows
update Y set
Parent_Id = T2.New_ID
from #T as T1
inner join #T as T2
on T1.Old_ParentID = T2.Old_ID
inner join YourTable as Y
on T1.New_ID = Y.ID;
SE-Data
Working example. Works in theory but probably not robust for high transaction volumes or fast enough for big tables.
--- sample table
create table tbl (
id int identity primary key,
parent_id int references tbl(id),
topic_name varchar(100));
insert tbl values
( Null, 'Topic 1'),
( 1, ' Topic 2'),
( 2 , ' Topic 3'),
( 3 , ' Topic 4'),
( 2 , ' Topic 5'),
( Null, ' Topic 6'),
( 4, ' Topic 4-3'),
( 7, ' Topic 5-4')
;
--- script to duplicate a hierarchy branch
declare #inserttbl table (
id int,
parent_id int,
topic_name varchar(100));
;with cte as (
select id, parent_id, topic_name
from tbl
where id=2 -- or parameter
union all
select t.id, t.parent_id, t.topic_name
from tbl t
join cte c on t.parent_id=c.id
), cte2 as (
select *,rn=row_number() over (order by id)
from cte
), cte3 as (
select rec.*, par.rn as parent_rn
from cte2 rec
left join cte2 par on par.id=rec.parent_id
)
insert #inserttbl
select cte3.rn,
case when cte3.rn=1 then cte3.parent_id
else cte3.parent_rn end,
topic_name
from cte3;
insert tbl(topic_name)
select topic_name from #inserttbl order by id;
declare #delta int=scope_identity()-##rowcount;
update t
set parent_id = i.parent_id + case when i.id=1 then 0
else #delta end
from tbl t
join #inserttbl i on t.id - #delta = i.id;
--- check results
select * from tbl;

CTE to return all items in hierarchy

I have a table with a recursive hierarchy (i.e. ID, ParentID). For any item in this hierachy, I want to be able to bring back a list of everything UP AND DOWN the hierarchy along with the level for each row. Assume that a parent can only ever have a single child.
Take for example the following:
ID ParentID
--------------
1 NULL
2 1
3 2
4 NULL
5 4
6 5
Given ID 1, 2, or 3, I want to return:
ID ParentID Level
-----------------------
1 NULL 1
2 1 2
3 2 3
I've done this before, but I can't remember how. I know the solution involves a CTE, I just can't get it right! Any help is appreciated.
;with cte as
(
select *, 1 as level from #t where id = #yourid
union all
select t.*, level - 1
from cte
inner join #t t on cte.parent = t.id
),
cte2 as
(
select * from cte
union all
select t.*, level+1
from cte2
inner join #t t on cte2.id = t.parent
)
select id,parent, ROW_NUMBER() over (order by level) level
from ( select distinct id, parent, level from cte2) v
The most barebones version of the CTE query I could come up with is:
WITH Ancestry (AncestorID, DescendantID)
AS
(
SELECT
ParentID, ID
FROM
dbo.Location
WHERE
ParentID IS NOT NULL
UNION ALL
SELECT
P.AncestorID, C.ID
FROM
dbo.Location C
JOIN
Ancestry P on C.ParentID = P.DescendantID
)
SELECT * FROM Ancestry
The result is a list of all Ancestor/Descendant relationships that exist in the table.
The final "SELECT * FROM Ancestry" can be replaced with something more complex to filter, order, etc.
To include reflexive relationships, the query can be modified by adding two lines to the final SELECT statement:
SELECT * FROM Ancestry
UNION
SELECT ID, ID FROM dbo.Location
;WITH Recursive_CTE AS (
SELECT
child.ExecutiveId,
CAST(child.ExecutiveName as varchar(100)) BusinessUnit,
CAST(NULL as bigint) ParentUnitID,
CAST(NULL as varchar(100)) ParentUnit,
CAST('' as varchar(100)) LVL,
CAST(child.ExecutiveId as varchar(100)) Hierarchy,
1 AS RecursionLevel
FROM Sales_Executive_level child
WHERE ExecutiveId = 4000 --your Id which you want to get all parent node
UNION ALL
SELECT
child.ExecutiveId,
CAST(LVL + child.ExecutiveName as varchar(100)) AS BusinessUnit,
child.ParentExecutiveID,
parent.BusinessUnit ParentUnit,
CAST('' + LVL as varchar(100)) AS LVL,
CAST(Hierarchy + ':' + CAST(child.ExecutiveId as varchar(100)) as varchar(100)) Hierarchy,
RecursionLevel + 1 AS RecursionLevel
FROM Recursive_CTE parent
INNER JOIN Sales_Executive_level child ON child.ParentExecutiveID = parent.ExecutiveId
)
SELECT * FROM Recursive_CTE ORDER BY Hierarchy
OPTION (MAXRECURSION 300);

SQL Server CTE -Find top parentID forEach childID?

I have a table which contains hierarchy data - something like:
childID | parentID
____________________
1 | 5
5 | 9
9 | 20
2 | 4
3 | 7
7 | 8
8 | 8
20 | 20
4 | 4
8 | 8
desired output:
I've created a recursive CTE which finds me the top fatherID.
Something like:
;WITH cte AS (
SELECT a.childID
,a.parentID
,1 AS lvl
FROM [Agent_Agents] a
WHERE a.childID = 214 //<==== value to begin with !! - thats part the problem
UNION ALL
SELECT tmp.childID
,tmp.parentID
,cte.lvl+1
FROM [Agent_Agents] tmp
INNER JOIN cte ON tmp.childID = cte.parentID
WHERE cte.childID<>cte.parentID
)
SELECT *
FROM cte
WHERE lvl = (
SELECT MAX(lvl)
FROM cte
)
The problem:
I executed the CTE with explicit childID value to begin with (214) !
So it gives me the value for 214 only.
the CTE do the recursive part and find topParent for childID.
but
I want ForEach row in the Table - to execute the CTE with the childID value !
I have tried to do it with CROSS APPLY:
Something like:
select * from myTable Cross Apply (
;WITH cte AS (....)
)
but IMHO (from my testing !!) - its impossible.
The other idea of putting the recursive CTE in a UDF has a performance penalty (udf's problem as we know).
How can I create this query so that it'll actually work? ( or some near solution )?
here is what I've tried
https://data.stackexchange.com/stackoverflow/query/edit/69458
Can't you do something like this?
;WITH cte AS (....)
SELECT
*
FROM
cte
CROSS APPLY
dbo.myTable tbl ON cte.XXX = tbl.XXX
Put the CROSS APPLY after the CTE definition - into the one SQL statement that refers back to the CTE. Wouldn't that work??
OR: - flip around your logic - do a "top-down" CTE, that picks the top-level nodes first, and then iterates through the hiearchy. This way, you can easily determine the "top-level father" in the first part of the recursive CTE - something like this:
;WITH ChildParent AS
(
SELECT
ID,
ParentID = ISNULL(ParentID, -1),
SomeName,
PLevel = 1, -- defines level, 1 = TOP, 2 = immediate child nodes etc.
TopLevelFather = ID -- define "top-level" parent node
FROM dbo.[Agent_Agents]
WHERE ParentID IS NULL
UNION ALL
SELECT
a.ID,
ParentID = ISNULL(a.ParentID, -1),
a.SomeName,
PLevel = cp.PLevel + 1,
cp.TopLevelFather -- keep selecting the same value for all child nodes
FROM dbo.[Agent_Agents] a
INNER JOIN ChildParent cp ON r.ParentID = cp.ID
)
SELECT
ID,
ParentID,
SomeName,
PLevel,
TopLevelFather
FROM ChildParent
This would give you nodes something like this (based on your sample data, slightly extended):
ID ParentID SomeName PLevel TopLevelFather
20 -1 Top#20 1 20
4 -1 TOP#4 1 4
8 -1 TOP#8 1 8
7 8 ChildID = 7 2 8
3 7 ChildID = 3 3 8
2 4 ChildID = 2 2 4
9 20 ChildID = 9 2 20
5 9 ChildID = 5 3 20
1 5 ChildID = 1 4 20
Now if you select a particular child node from this CTE output, you'll always get all the infos you need - including the "level" of the child, and its top-level parent node.
Not sure I understand what you are looking for but it could be this.
;WITH c
AS (SELECT childid,
parentid,
parentid AS topParentID
FROM #myTable
WHERE childid = parentid
UNION ALL
SELECT T.childid,
T.parentid,
c.topparentid
FROM #myTable AS T
INNER JOIN c
ON T.parentid = c.childid
WHERE T.childid <> T.parentid)
SELECT childid,
topparentid
FROM c
ORDER BY childid
SE-Data
It is the same as answer by marc_s with the difference that I use your table variable and the fact that you have childID = parentID for root nodes where the answer by marc_s has parent_ID = null for root nodes. In my opinion it is better to have parent_ID = null for root nodes.
I have not yet the time to look further into your question and am not sure whether or not i've understood your problem, but couldn't you use this svf to get the top father's id?
CREATE FUNCTION [dbo].[getTopParent] (
#ChildID INT
)
RETURNS int
AS
BEGIN
DECLARE #result int;
DECLARE #ParentID int;
SET #ParentID=(
SELECT ParentID FROM ChildParent
WHERE ChildID = #ChildID
)
IF(#ParentID IS NULL)
SET #result = #ChildID
ELSE
SET #result = [dbo].[getTopParent](#ParentID)
RETURN #result
END
Then you should be able to find each top parent in this way:
SELECT ChildID
, [dbo].[getTopParent](ChildID) AS TopParentID
FROM ChildParent
select distinct
a.ChildID,a.ParentID,
--isnull(nullif(c.parentID,b.parentID),a.parentID) as toppa,
B.parentID
--,c.parentID
,isnull(nullif(d.parentID,a.parentID),c.parentID) as toppa1,a.name
from myTable a
inner join myTable c
on a.parentID=c.parentID
inner join myTable b
on b.childID=a.parentID
inner join myTable d
on d.childID=b.parentID
I have using the without CTE expression and then using joins to get the step to step parent for child and then more important Common table expressions were introduced in SQL Server 2005 not in server 2000 so using joins to get values this is basic way for to get parentid for a child value
select dbo.[fn_getIMCatPath](8)
select Cat_id,Cat_name,dbo.[fn_getIMCatPath](cat_id) from im_category_master
Create FUNCTION [dbo].[fn_getIMCatPath] (#ID INT)
returns NVARCHAR(1000)
AS
BEGIN
DECLARE #Return NVARCHAR(1000),
#parentID INT,
#iCount INT
SET #iCount = 0
SELECT #Return = Cat_name,
#parentID = parent_id
FROM im_category_master
WHERE [cat_id] = #ID
WHILE #parentID IS NOT NULL
BEGIN
SELECT #Return = cat_name + '>' + #Return,
#parentID = parent_id
FROM im_category_master
WHERE [cat_id] = #parentID
SET #iCount = #iCount + 1
IF #parentID = -1
BEGIN
SET #parentID = NULL
END
IF #iCount > 10
BEGIN
SET #parentID = NULL
SET #Return = ''
END
END
RETURN #Return
END
Consider this sample data and respective SQL to access child records along with their top parent.
Sample DATA
SQL code:
;WITH c AS (
SELECT Id, Name, ParentId as CategoryId,
Id as MainCategoryId, Name AS MainCategory
FROM pmsItemCategory
WHERE ParentId is null
UNION ALL
SELECT T.Id, T.Name, T.ParentId, MainCategoryId, MainCategory
FROM pmsItemCategory AS T
INNER JOIN c ON T.ParentId = c.Id
WHERE T.ParentId is not null
)
SELECT Id, Name, CategoryId, MainCategoryId, MainCategory
FROM c
order by Id
select distinct
a.ChildID,a.ParentID,
--isnull(nullif(c.parentID,b.parentID),a.parentID) as toppa,
B.parentID
--,c.parentID
,isnull(nullif(d.parentID,a.parentID),c.parentID) as toppa1,a.name
from myTable a
inner join myTable c
on a.parentID=c.parentID
inner join myTable b
on b.childID=a.parentID
inner join myTable d
on d.childID=b.parentID
With cte as
(
Select ChileId,Name,ParentId from tblHerarchy
where ParentId is null
union ALL
Select h.ChileId,h.Name,h.ParentId from cte
inner join tblHerarchy h on h.ParentId=cte.ChileId
)
Select * from cte
With cteherarchy as
(
Select ChileId,Name,ParentId from tblHerarchy
where ParentId is null
union ALL
Select h.ChileId,h.Name,h.ParentId from cte
inner join tblHerarchy h on h.ParentId=cte.ChileId
)
Select * from cteherarchy

Resources