I have a Standard table, which sotres parent, child category relationship...like this.
id, parent, catName, sort
And I use the following query to create a recursive tree
;WITH cte AS (
SELECT 0 AS lvl, id, catName, parent,levels,sort,
CAST(id AS VARCHAR(128)) AS path
FROM CategoriesMap WHERE parent =0
UNION ALL
SELECT p.lvl + 1, c.id, c.catName, c.parent,c.levels,c.sort,
CAST(p.path + '_' + CAST(c.id AS VARCHAR) AS VARCHAR(128))
FROM CategoriesMap c
INNER JOIN cte p ON p.id = c.parent
)
SELECT
id,
catName AS catName,
lvl,
levels,
path,
parent,
sort
FROM cte
ORDER BY path
And the output is like this Image:
Look for the Row with value ASP.NET & CLASSIC ASP, these are the last leaf(children) for the technology > Software (parents), I want to sort the LAST CHILDREN of any given parent (last parent). I can have multiple parents for a given node (last child) & all I care about is sort the LAST Children (leaf) using the "Sort" column.
so basicly "Classic Asp" shoud be before "Asp.Net" (Last Column is SORT column in my image).
My query is fine, it returns the results as expected...only challenege is i want to SORT the last NODE using the SORT column in table, last node can have 3 or 4 children which I want to sort, all nodes above the last node are its parents (which are in correct order already).
I want output like this.... Internet > ISP's > CableVision (1) :
Verizon (2) as you can see CableVision & Verizon have Sort Value of 1
& then 2, Now lets say we have Shopping > Coupons > Macys(0) : Sears
(2), same thing....I want Macys & Sears to be sorted...and its pretty
obvious their parents are Shopping > Coupons.
#Richard aka cyberkiwi, after applying your code, my sorting for Categories table is very random. output is below
Delaying the calculation of the path for one level, so the final result set has the parent path (ppath) available:
;WITH cte AS (
SELECT 0 AS lvl, id, catName, parent,levels,sort,
CAST('' AS VARCHAR(128)) AS ppath
FROM CategoriesMap WHERE parent =0
UNION ALL
SELECT p.lvl + 1, c.id, c.catName, c.parent,c.levels,c.sort,
CAST(p.ppath + '_' + CAST(p.id AS VARCHAR) AS VARCHAR(128))
FROM CategoriesMap c
INNER JOIN cte p ON p.id = c.parent
)
SELECT
id,
catName,
lvl,
levels,
CAST(ppath + '_' + CAST(id AS VARCHAR) AS VARCHAR(128)) AS path,
parent,
sort
FROM cte
ORDER BY
CASE WHEN sort IS NULL
THEN path
ELSE ppath
END
, sort ;
Not really sure why the above gives error. This will not:
ORDER BY
CASE WHEN sort IS NULL
THEN CAST(ppath + '_' + CAST(id AS VARCHAR) AS VARCHAR(128))
ELSE ppath
END
, sort ;
This SQL Fiddle should give you what you need.
The trick really is when you mix leaves with branches. In my solution, leaves ALWAYS appear before branches, and within the leaves (even when inter-mixed with branches), they are sorted by the sort column of course.
DDL
create table CategoriesMap(
id int, parent int, catname varchar(20), sort int);
insert CategoriesMap select
1, 0, 'Activities', null union all select
2, 0, 'Property', null union all select
3, 2, 'For rent', null union all select
4, 2, 'For sale', null union all select
12, 0, 'Technology', 3 union all select
15, 12, 'Hardware', null union all select
21, 12, 'Phones', null union all select
22, 15, 'Computers', null union all select
18, 12, 'Software', null union all select
19, 18, 'Asp.net', 2 union all select
20, 18, 'SQL', 3 union all select
23, 18, 'Php', 4 union all select
24, 18, 'Classic ASP', 1;
Query
;WITH leaves AS (
SELECT A.id
FROM CategoriesMap A
LEFT JOIN CategoriesMap B ON A.id=B.parent
WHERE B.id is null
)
,cte AS (
SELECT 0 AS lvl, id, catName, parent,sort,
CAST(id AS VARCHAR(MAX)) AS path,
'/'+CAST(id AS VARCHAR(MAX))+'/' AS hier
FROM CategoriesMap
WHERE parent =0
UNION ALL
SELECT p.lvl + 1, c.id, c.catName, c.parent,c.sort,
p.path + '_' + CAST(c.id AS VARCHAR(MAX)),
p.hier + CAST(c.id AS VARCHAR(MAX)) + '/'
FROM CategoriesMap c
JOIN cte p ON p.id = c.parent
)
SELECT c.id,
c.catName,
c.lvl,
--levels,
c.path,
--c.hier,
c.parent,
c.sort
FROM cte c
LEFT JOIN leaves l on l.id=c.id
ORDER BY CASE WHEN l.id is null
then cast(hier as hierarchyid)
else cast(hier as hierarchyid).GetAncestor(1)
END,
CASE WHEN l.id is null then 0 else 1 end,
sort
Related
I have a tree table. And, I am going to get root and top level on this tree.
Help with the solution you can use anything you want
declare #disc table (
id int,
parent int,
label varchar(50)
)
insert into #disc
select *
from (
values (1, null, 'q_1'),
(2, 1, 'a_1_1'),
(3, 2, 'a_1_1_1'),
(4, 1, 'a_1_2'),
(5, null, 'q_5'),
(6, 5, 'a_5_1'),
(7, 5, 'a_5_2')
) x (id, parent, label);
1. q_1
2. a_1_1
3. a_1_1_1
4. a_1_2
5. q_5
6. a_5_1
7. a_5_2
And, my result should be like this:
1: 1, null, q_1
2: 2, 1, a_1_1
3: 5, null, q_5
4: 6, 5, a_5_1
or
1: 1, null, q_1
2: 5, null, q_5
3: 2, 1, a_1_1
4: 6, 5, a_5_1
I only found one way, but I believe there is a better solution:
with rec as (
select id, parent, label,
row_number() over(order by id) rnk,
1 lvl
from #disc
where parent is null
union all
select d.id, d.parent, d.label,
row_number() over(order by d.id) rnk,
r.lvl + 1
from rec r
join #disc d on r.id = d.parent
)
select *
from rec
where parent is null or (rnk = 1 and lvl = 2)
If I understand this, the parent value will be null in the root nodes. The next level down will have a root node as parent. So ...
;with roots as
(
select id, parent, label
from #disc
where parent is null
)
select id, parent, label
from roots
union
select id, parent, label
from #disc
where parent in (select id from roots)
It doesn't look like you actually want to recurse here.
You can just do a self-join inside an apply.
select
row_number() over (order by isnull(c.parent, c.id), c.id),
c.id,
c.parent,
c.label
from #disc p
cross apply (
select p.id, p.parent, p.label
union all
select top 1 c.id, c.parent, c.label
from #disc c
where p.id = c.parent
order by c.id
) c
where p.parent is null;
db<>fiddle
This is my table:
CREATE TABLE IF NOT EXISTS NODE
(
UUID VARCHAR NOT NULL,
PARENT_UUID VARCHAR NULL,
NAME VARCHAR NOT NULL,
PRIMARY KEY (UUID)
);
This is my test data:
INSERT INTO node (uuid, parent_uuid, name)
VALUES
('dfca05bc-551d-4e3d-87aa-7dd7d29539f6', null, 'Computers'),
('ff83eb99-ea2c-4d11-8ebe-4600445a3bda', null, 'Food'),
('405f0267-fa22-4cac-a397-c430be221828', null, 'Drinks'),
('e79ecefa-c3e2-400f-aab4-2d28fcd3a832', 'dfca05bc-551d-4e3d-87aa-7dd7d29539f6', 'Monitors'),
('61a94b77-56c2-48ff-b869-39305648a25c', 'dfca05bc-551d-4e3d-87aa-7dd7d29539f6', 'System blocks'),
('5d88a9b7-7ffe-45e5-b35f-9350072ed619', 'dfca05bc-551d-4e3d-87aa-7dd7d29539f6', 'Mother boards'),
('5994c39d-c4ea-454c-ae57-118392b93f66', 'dfca05bc-551d-4e3d-87aa-7dd7d29539f6', 'Different'),
('d1c994fe-f3ec-40ed-aace-5221c026c0ea', '5994c39d-c4ea-454c-ae57-118392b93f66', 'Keyboards'),
('7757aa9b-abee-4d30-89d3-79f77613b5e8', '5994c39d-c4ea-454c-ae57-118392b93f66', 'Mice'),
('33d93c3a-1c2d-44b9-8fac-3f83074104a5', '5994c39d-c4ea-454c-ae57-118392b93f66', 'Joysticks'),
('f13bb023-47b2-473a-83b0-4223ff6e28b9', 'e79ecefa-c3e2-400f-aab4-2d28fcd3a832', 'Size 14'),
('312a4e56-71ef-4372-a556-17ace15197e6', 'e79ecefa-c3e2-400f-aab4-2d28fcd3a832', 'Size 15'),
('c525374c-6a06-46e4-98c8-bc669e811e22', 'e79ecefa-c3e2-400f-aab4-2d28fcd3a832', 'Size 16');
I want to get a) all parents and their siblings b) all the siblings of the node. For example, if I have Joysticks node is selected, then I want to get
Computers
Different
Joysticks
Keyboards
Mice
Monitors
Mother boards
System blocks
Drinks
Food
As you see Size 14, 15, 16 nodes that belong to monitors are not selected.
Up to now I have the following query:
WITH RECURSIVE theparents (uuid, parent_uuid, name, level) AS (
SELECT uuid, parent_uuid, name, 0 AS level,
FROM node
WHERE uuid = '33d93c3a-1c2d-44b9-8fac-3f83074104a5'
UNION ALL
SELECT a.uuid, a.parent_uuid, a.name, b.level + 1 as level
FROM node a
INNER JOIN theparents b ON b.parent_uuid = a.uuid
),
thesiblings AS (
SELECT a.uuid, a.parent_uuid, a.name, b.level as level
FROM node a
INNER JOIN theparents b ON b.parent_uuid = a.parent_uuid OR (b.parent_uuid IS NULL AND a.parent_uuid IS NULL)
)
SELECT * FROM thesiblings;
This query selects all nodes, but without sorting. Is it possible to sort them using sql or it can be done only manually?
You can just join the tree to node. The only problem is to keep it sorted according to tree traversal order. Try this, tested in MySql 8.0
EDIT
Now sorting by names-based path, 20 is max name length in the table
WITH RECURSIVE tree (uuid, parent_uuid, name, level, path) AS
(
SELECT uuid, parent_uuid, name, 0 level, cast(Rpad(name, 20, ' ') as char(200)) path
FROM nodes
WHERE uuid = '33d93c3a-1c2d-44b9-8fac-3f83074104a5'
UNION ALL
SELECT a.uuid, a.parent_uuid, a.name, level-1, concat(Rpad(a.name, 20, ' '),'>', path)
FROM nodes a
INNER JOIN tree b ON b.parent_uuid = a.uuid
)
select uuid, name, level /*, path */
from (
select n.uuid, n.name
, max(-level) over() + level + 1 level
, concat(substring(first_value(path) over(order by level), 1, (20+1) * (max(-level) over() + level +1 )), n.name) path
from tree t
join nodes n on n.parent_uuid = t.uuid
-- no children for starting node
and t.level <> 0
union all
-- roots
select uuid, name, 0, Rpad(name, 20, ' ')
from nodes
where parent_uuid is null
) t
order by path
db<>fiddle
I'm trying to generate a path from the name of an item's parents. For example if test has for parent dad the path would be dad/test; and if dad had for parent gran the path of test would be gran/dad/test.
I only have the id of the child, so far I only have a query which generates the paths of everyone recursively and then selects the right one but that doesn't really seem efficient.
WITH SubItems
AS (
SELECT CAST([Name] AS VARCHAR(255)) AS [Path],
Id,
ParentId,
0 AS Depth
FROM Items
WHERE Id = 1 -- First parent of everyone
UNION ALL
SELECT CAST(CONCAT(parent.[Path], '/', sub.[Name]) AS VARCHAR(255)),
sub.Id,
sub.ParentId,
parent.Depth + 1
FROM Items sub
JOIN SubItems parent ON parent.Id = sub.ParentId
)
SELECT [Path]
FROM SubItems
WHERE Id = 1425 -- SubItem I want the path of
I can also go upwards, which would be faster but I can't create the path this way. I could try to concatenate all the results ordered by the "depth" but again this doesn't seem right.
DECLARE #Path;
WITH ParentItems
AS (
SELECT [Name],
Id,
ParentId,
0 AS Depth
FROM Items
WHERE Id = 1425 -- SubItem I want the path of
UNION ALL
SELECT [Name],
parent.Id,
parent.ParentId,
sub.Depth - 1
FROM Items parent
JOIN ParentItems sub ON sub.ParentId = parent.Id
)
SELECT #Path = COALESCE(#Path + '/', '') + [Name]
FROM ParentItems
ORDER BY Depth;
SELECT #Path;
Is there a way to go upwards recursively?
Something like this for example, where ParentPath would be equal to CONCAT(ParentPath, '/', [Path]) again:
WITH ...
SELECT CONCAT(ParentPath, '/', [Name])
FROM Items
I know in C# you could do something like:
function getPath() {
return (parent?.getPath() ?? "") + "/" + this.Name;
}
Edit: Why I can't construct the path going up, like this:
WITH ParentItems AS (
SELECT i.Name, i.Id, i.ParentId, 0 AS Depth,
CONVERT(VARCHAR(MAX), i.Name) as path
FROM Items i
WHERE i.Id = 1425 -- SubItem I want the path of
UNION ALL
SELECT i.Name, i.Id, i.ParentId, pi.Depth - 1,
CONCAT(pi.Name, '/', i.[Path])
FROM Items i JOIN
ParentItems pi
ON pi.ParentId = parent.Id
)
SELECT *
FROM ParentItems
ORDER BY Depth;
Assuming the example from above where gran is parent to dad is parent to test, the result of this query would be:
| name | path |
|------|---------------|
| gran | gran/dad/test |
| dad | dad/test |
| test | test |
While it should be the opposite:
| name | path |
|------|---------------|
| gran | gran/ |
| dad | gran/dad |
| test | gran/dad/test |
This is because of the way the query passes the name of the child upwards, adding it to the path of its parent rather than the opposite.
Why can't you construct the path going up?
WITH ParentItems AS (
SELECT i.Name, i.Id, i.ParentId, 0 AS Depth,
CONVERT(VARCHAR(MAX), i.Name) as path
FROM Items i
WHERE i.Id = 1425 -- SubItem I want the path of
UNION ALL
SELECT i.Name, i.Id, i.ParentId, pi.Depth + 1,
CONCAT(i.Name, '/', pi.path)
FROM Items i JOIN
ParentItems pi
ON pi.ParentId = parent.Id
)
SELECT *
FROM ParentItems
ORDER BY Depth DESC;
For future references, using FOR XML PATH('') seems to work.
WITH ParentItems
AS (
SELECT [Name],
Id,
ParentId,
0 AS Depth
FROM Items
WHERE Id = 1425 -- SubItem I want the path of
UNION ALL
SELECT [Name],
parent.Id,
parent.ParentId,
sub.Depth - 1
FROM Items parent
JOIN ParentItems sub ON sub.ParentId = parent.Id
)
SELECT (
SELECT '/' + [Name]
FROM ParentItems
ORDER BY Depth
FOR XML PATH('')
)
The following code:
Walks the tree from the child up to the oldest ancestor while assembling a path.
Gets the path to the oldest ancestor and splits it into individuals.
Walks the list of individuals from the oldest ancestor back down to the starting child while assembling the path.
NB: This code does not use String_Split because it is documented thusly: "The output rows might be in any order. The order is not guaranteed to match the order of the substrings in the input string." A Jeff Moden string splitter is used which guarantees the order of the results.
Note that you can select the results of any of the intermediate CTEs in order to see how the process proceeds. Just replace the final select statement with one of the alternatives provided in comments.
Confession: I didn't try to generate the curious dangling solidus in the first row of the desired output ("gran/") rather than the more consistent "gran". It is assumed to be a typographical error in the sample data.
-- Sample data.
declare #Samples as Table ( Id Int Identity, Name VarChar(10), ParentName VarChar(10) );
insert into #Samples ( Name, ParentName ) values
( 'test', 'dad' ),
( 'dad', 'gran' ),
( 'gran', null );
select * from #Samples;
-- Starting point.
declare #ChildName as VarChar(10) = 'test';
-- Walk the tree.
with
Tree as (
-- Note that paths in this initial tree are built using Id , not Name .
-- This keeps the path length down, ensures rows are uniquely identified, avoids problems with "funny" names, ... .
-- Start at the target child name.
select Id, Name, ParentName, 0 as Depth,
Cast( Id as VarChar(100) ) as Path
from #Samples
where Name = #ChildName
union all
-- Walk up the tree one level at a time.
select S.Id, S.Name, S.ParentName, T.Depth + 1,
Cast( Cast( S.Id as VarChar(100) ) + '/' + T.Path as VarChar(100) )
from Tree as T inner join
#Samples as S on S.Name = T.ParentName
),
TreePath as (
-- Take the path of the oldest ancestor and split it apart.
select ItemNumber, Cast( Item as Int ) as Item from Tree as T cross apply
dbo.DelimitedSplit8K( T.Path, '/' ) where T.ParentName is NULL ),
InvertedTree as (
-- Start at the first item on path, i.e. the oldest ancestor.
select S.Name, 1 as Depth,
Cast( S.Name as VarChar(100) ) as Path
from TreePath as TP inner join
#Samples as S on S.Id = TP.Item
where TP.ItemNumber = 1
union all
-- Add chldren on the way down.
select S.Name, IT.Depth + 1,
Cast( IT.Path + '/' + S.Name as VarChar(100) )
from InvertedTree as IT inner join
TreePath as TP on TP.ItemNumber = IT.Depth + 1 inner join
#Samples as S on S.Id = TP.Item
)
-- To see the intermediate results use one of the following select statements:
-- select * from Tree;
-- select * from TreePath;
-- select * from InvertedTree;
select Name, Path
from InvertedTree
order by Depth;
The Jeff Moden string splitter:
CREATE FUNCTION [dbo].[DelimitedSplit8K]
--===== Define I/O parameters
(#pString VARCHAR(8000), #pDelimiter VARCHAR(16))
--WARNING!!! DO NOT USE MAX DATA-TYPES HERE! IT WILL KILL PERFORMANCE!
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000...
-- enough to cover VARCHAR(8000)
WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), --10E+1 or 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
-- for both a performance gain and prevention of accidental "overruns"
SELECT TOP (ISNULL(DATALENGTH(#pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
SELECT 1 UNION ALL
SELECT t.N+ Len( #pDelimiter ) FROM cteTally t WHERE SUBSTRING(#pString,t.N, Len( #pDelimiter ) ) = #pDelimiter
),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
SELECT s.N1,
ISNULL(NULLIF(CHARINDEX(#pDelimiter,#pString,s.N1),0)-s.N1 ,8000)
FROM cteStart s
)
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
Item = SUBSTRING(#pString, l.N1, l.L1)
FROM cteLen l
;
I need to identify duplicate sets of data and give those sets who's data is similar a group id.
id threshold cost
-- ---------- ----------
1 0 9
1 100 7
1 500 6
2 0 9
2 100 7
2 500 6
I have thousands of these sets, most are the same with different id's. I need find all the like sets that have the same thresholds and cost amounts and give them a group id. I'm just not sure where to begin. Is the best way to iterate and insert each set into a table and then each iterate through each set in the table to find what already exists?
This is one of those cases where you can try to do something with relational operators. Or, you can just say: "let's put all the information in a string and use that as the group id". SQL Server seems to discourage this approach, but it is possible. So, let's characterize the groups using:
select d.id,
(select cast(threshold as varchar(8000)) + '-' + cast(cost as varchar(8000)) + ';'
from data d2
where d2.id = d.id
for xml path ('')
order by threshold
) as groupname
from data d
group by d.id;
Oh, I think that solves your problem. The groupname can serve as the group id. If you want a numeric id (which is probably a good idea, use dense_rank():
select d.id, dense_rank() over (order by groupname) as groupid
from (select d.id,
(select cast(threshold as varchar(8000)) + '-' + cast(cost as varchar(8000)) + ';'
from data d2
where d2.id = d.id
for xml path ('')
order by threshold
) as groupname
from data d
group by d.id
) d;
Here's the solution to my interpretation of the question:
IF OBJECT_ID('tempdb..#tempGrouping') IS NOT NULL DROP Table #tempGrouping;
;
WITH BaseTable AS
(
SELECT 1 id, 0 as threshold, 9 as cost
UNION SELECT 1, 100, 7
UNION SELECT 1, 500, 6
UNION SELECT 2, 0, 9
UNION SELECT 2, 100, 7
UNION SELECT 2, 500, 6
UNION SELECT 3, 1, 9
UNION SELECT 3, 100, 7
UNION SELECT 3, 500, 6
)
, BaseCTE AS
(
SELECT
id
--,dense_rank() over (order by threshold, cost ) as GroupId
,
(
SELECT CAST(TblGrouping.threshold AS varchar(8000)) + '/' + CAST(TblGrouping.cost AS varchar(8000)) + ';'
FROM BaseTable AS TblGrouping
WHERE TblGrouping.id = BaseTable.id
ORDER BY TblGrouping.threshold, TblGrouping.cost
FOR XML PATH ('')
) AS MultiGroup
FROM BaseTable
GROUP BY id
)
,
CTE AS
(
SELECT
*
,DENSE_RANK() OVER (ORDER BY MultiGroup) AS GroupId
FROM BaseCTE
)
SELECT *
INTO #tempGrouping
FROM CTE
-- SELECT * FROM #tempGrouping;
UPDATE BaseTable
SET BaseTable.GroupId = #tempGrouping.GroupId
FROM BaseTable
INNER JOIN #tempGrouping
ON BaseTable.Id = #tempGrouping.Id
IF OBJECT_ID('tempdb..#tempGrouping') IS NOT NULL DROP Table #tempGrouping;
Where BaseTable is your table, and and you don't need the CTE "BaseTable", because you have a data table.
You may need to take extra-precautions if your threshold and cost fields can be NULL.
I am looking to return a query that shows exactly like this:
Root 1
--> Child 2
----->Child 3
Root 2
--> Child 4
---->Child 5
So the query should return Root 1 as One row, ---> Child 2 as another row. Assume n Levels, and "--->" format is placed for each child. Level is higher then "---->" increases.
My Table definition is
[NodeId, ParentId, Name, Level]
On SQL Server 2008 and above, you can use hierarchyId datatype to quickly achieve the desired sorting. You can use REPLICATE() to get the dashes.
;with cte as (
select NodeId, ParentId, Name, 0 Level, '/' + cast(NodeId as varchar(max)) + '/' Hier
from tbl1
where ParentId is null
union all
select t.NodeId, t.ParentId, t.Name, Level+1, Hier + cast(t.NodeId as varchar(max)) + '/'
from tbl1 t
join cte c on t.ParentId = c.NodeId
)
select case when level=0
then ''
else replicate('-',level*2) + '>' end + Name
from cte
order by cast(Hier as hierarchyid);
SQL Fiddle
On earlier SQL Server 2005, you can emulate the hierarchyId sorting using zero-padded strings:
;with cte as (
select NodeId, ParentId, Name, 0 Level, right(replicate('0',10)+cast(NodeId as varchar(max)),11) Hier
from tbl1
where ParentId is null
union all
select t.NodeId, t.ParentId, t.Name, Level+1, Hier + right(replicate('0',10)+cast(t.NodeId as varchar(max)),11)
from tbl1 t
join cte c on t.ParentId = c.NodeId
)
select case when level=0
then ''
else replicate('-',level*2) + '>' end + Name
from cte
order by Hier;