This does use CTEs, but hopefully I can explain their usage:
;With ExistingQuery (id,parent,sort) as (
select 1,null,0 union all
select 2,null,1 union all
select 3,1 ,0 union all
select 4,1 ,1 union all
select 5,3 ,0 union all
select 6,5 ,0 union all
select 7,2 ,0
), Reord as (
select *,ROW_NUMBER() OVER (ORDER BY parent,sort) as rn from ExistingQuery
), hier as (
select id,parent,'/' + CONVERT(varchar(max),rn)+'/' as part
from Reord
union all
select h.id,r.parent,'/' + CONVERT(varchar(max),r.rn) + part
from hier h
inner join
Reord r
on
h.parent = r.id
)
select
e.*
from
hier h
inner join
ExistingQuery e
on
h.id = e.id
where
h.parent is null
order by CONVERT(hierarchyid,h.part)
ExistingQuery
is just whatever you've currently got for your query. You should be able to just place your existing query in there (possibly with an expanded column list) and everything should just work.
Reord
addresses a concern of mine but it may not be needed - if your actual data is actually such that the id
values are indeed in the right order that we can ignore sort
then remove Reord
and replace the references to rn
with id
. But this CTE does that work to make sure that the children of parents are respecting the sort
column.
Finally, the hier
CTE is the meat of this solution - for every row, it's building up a hierachyid
for that row - from the child, working back up the tree until we hit the root.
And once the CTEs are done with, we join back to ExistingQuery
so that we're just getting the data from there, but can use the hierarchyid
to perform proper sorting - that type already knows how to correctly sort hierarchical data.
Result:
id parent sort
----------- ----------- -----------
1 NULL 0
3 1 0
5 3 0
6 5 0
4 1 1
2 NULL 1
7 2 0
And the result showing the part
column from hier
, which may help you see what that CTE constructed:
id parent sort part
----------- ----------- ----------- --------------
1 NULL 0 /1/
3 1 0 /1/3/
5 3 0 /1/3/6/
6 5 0 /1/3/6/7/
4 1 1 /1/4/
2 NULL 1 /2/
7 2 0 /2/5/
(You may also want to change the final SELECT
to just SELECT * from hier
to also get a feel for how that CTE works)