Normal hierachical approach:
select *
into emp
from
(values
(1, 'President', NULL),
(2, 'Vice President', 1),
(3, 'CEO', 2),
(4, 'CTO', 2),
(5, 'Group Project Manager', 4),
(6, 'Project Manager 1', 5),
(7, 'Project Manager 2', 5),
(8, 'Team Leader 1', 6),
(9, 'Software Engineer 1', 8),
(10, 'Software Engineer 2', 8),
(11, 'Test Lead 1', 6),
(12, 'Tester 1', 11),
(13, 'Tester 2', 11),
(14, 'Team Leader 2', 7),
(15, 'Software Engineer 3', 14),
(16, 'Software Engineer 4', 14),
(17, 'Test Lead 2', 7),
(18, 'Tester 3', 17),
(19, 'Tester 4', 17),
(20, 'Tester 5', 17)
) as x(emp_id, emp_name, mgr_id)
Query:
with recursive org(emp_id, emp_name, emp_level, mgr_id, sort) as
(
select
a.emp_id, a.emp_name, 0, a.mgr_id,
a.emp_name
from emp a
where a.mgr_id is null
union all
select
b.emp_id, b.emp_name, emp_level + 1, b.mgr_id,
sort || ' : ' || b.emp_name
from emp b
join org on org.emp_id = b.mgr_id
)
select
emp_id, repeat(' ', emp_level * 2) || emp_name as emp_name, sort
from org
order by sort
Output:
emp_id | emp_name | sort
--------+---------------------------------+--------------------------------------------------------------------------------------------------------------------
1 | President | President
2 | Vice President | President : Vice President
3 | CEO | President : Vice President : CEO
4 | CTO | President : Vice President : CTO
5 | Group Project Manager | President : Vice President : CTO : Group Project Manager
6 | Project Manager 1 | President : Vice President : CTO : Group Project Manager : Project Manager 1
8 | Team Leader 1 | President : Vice President : CTO : Group Project Manager : Project Manager 1 : Team Leader 1
9 | Software Engineer 1 | President : Vice President : CTO : Group Project Manager : Project Manager 1 : Team Leader 1 : Software Engineer 1
10 | Software Engineer 2 | President : Vice President : CTO : Group Project Manager : Project Manager 1 : Team Leader 1 : Software Engineer 2
11 | Test Lead 1 | President : Vice President : CTO : Group Project Manager : Project Manager 1 : Test Lead 1
12 | Tester 1 | President : Vice President : CTO : Group Project Manager : Project Manager 1 : Test Lead 1 : Tester 1
13 | Tester 2 | President : Vice President : CTO : Group Project Manager : Project Manager 1 : Test Lead 1 : Tester 2
7 | Project Manager 2 | President : Vice President : CTO : Group Project Manager : Project Manager 2
14 | Team Leader 2 | President : Vice President : CTO : Group Project Manager : Project Manager 2 : Team Leader 2
15 | Software Engineer 3 | President : Vice President : CTO : Group Project Manager : Project Manager 2 : Team Leader 2 : Software Engineer 3
16 | Software Engineer 4 | President : Vice President : CTO : Group Project Manager : Project Manager 2 : Team Leader 2 : Software Engineer 4
17 | Test Lead 2 | President : Vice President : CTO : Group Project Manager : Project Manager 2 : Test Lead 2
18 | Tester 3 | President : Vice President : CTO : Group Project Manager : Project Manager 2 : Test Lead 2 : Tester 3
19 | Tester 4 | President : Vice President : CTO : Group Project Manager : Project Manager 2 : Test Lead 2 : Tester 4
20 | Tester 5 | President : Vice President : CTO : Group Project Manager : Project Manager 2 : Test Lead 2 : Tester 5
(20 rows)
Now let's override sorting on Group Project Managers, let's make Project Manager 2 come before 1, and Project Manager 1 come after Project Manager 2. Let's also make tester 4 comes before 3, and tester 3 comes after tester 4
alter table emp add column order_override int null;
update emp set order_override = 1 where emp_id = 7; -- PM 2
update emp set order_override = 2 where emp_id = 6; -- PM 1
update emp set order_override = 1 where emp_id = 19; -- Tester 4
update emp set order_override = 2 where emp_id = 18; -- Tester 3
Query:
with recursive org(emp_id, emp_name, emp_level, mgr_id, sort) as
(
select
a.emp_id, a.emp_name, 0, a.mgr_id,
a.emp_name
from emp a
where a.mgr_id is null
union all
select
b.emp_id, b.emp_name, emp_level + 1, b.mgr_id,
sort || ' : ' || coalesce( lpad(order_override::text, 10, '0'), b.emp_name )
from emp b
join org on org.emp_id = b.mgr_id
)
select
emp_id, repeat(' ', emp_level * 2) || emp_name as emp_name, sort
from org
order by sort
Output:
emp_id | emp_name | sort
--------+---------------------------------+-------------------------------------------------------------------------------------------------------------
1 | President | President
2 | Vice President | President : Vice President
3 | CEO | President : Vice President : CEO
4 | CTO | President : Vice President : CTO
5 | Group Project Manager | President : Vice President : CTO : Group Project Manager
7 | Project Manager 2 | President : Vice President : CTO : Group Project Manager : 0000000001
14 | Team Leader 2 | President : Vice President : CTO : Group Project Manager : 0000000001 : Team Leader 2
15 | Software Engineer 3 | President : Vice President : CTO : Group Project Manager : 0000000001 : Team Leader 2 : Software Engineer 3
16 | Software Engineer 4 | President : Vice President : CTO : Group Project Manager : 0000000001 : Team Leader 2 : Software Engineer 4
17 | Test Lead 2 | President : Vice President : CTO : Group Project Manager : 0000000001 : Test Lead 2
19 | Tester 4 | President : Vice President : CTO : Group Project Manager : 0000000001 : Test Lead 2 : 0000000001
18 | Tester 3 | President : Vice President : CTO : Group Project Manager : 0000000001 : Test Lead 2 : 0000000002
20 | Tester 5 | President : Vice President : CTO : Group Project Manager : 0000000001 : Test Lead 2 : Tester 5
6 | Project Manager 1 | President : Vice President : CTO : Group Project Manager : 0000000002
8 | Team Leader 1 | President : Vice President : CTO : Group Project Manager : 0000000002 : Team Leader 1
9 | Software Engineer 1 | President : Vice President : CTO : Group Project Manager : 0000000002 : Team Leader 1 : Software Engineer 1
10 | Software Engineer 2 | President : Vice President : CTO : Group Project Manager : 0000000002 : Team Leader 1 : Software Engineer 2
11 | Test Lead 1 | President : Vice President : CTO : Group Project Manager : 0000000002 : Test Lead 1
12 | Tester 1 | President : Vice President : CTO : Group Project Manager : 0000000002 : Test Lead 1 : Tester 1
13 | Tester 2 | President : Vice President : CTO : Group Project Manager : 0000000002 : Test Lead 1 : Tester 2
(20 rows)
Without the sort column in data projection:
with recursive org(emp_id, emp_name, emp_level, mgr_id, sort) as
(
select
a.emp_id, a.emp_name, 0, a.mgr_id,
a.emp_name
from emp a
where a.mgr_id is null
union all
select
b.emp_id, b.emp_name, emp_level + 1, b.mgr_id,
sort || ' : ' || coalesce( lpad(order_override::text, 10, '0'), b.emp_name )
from emp b
join org on org.emp_id = b.mgr_id
)
select
emp_id, repeat(' ', emp_level * 2) || emp_name as emp_name
from org
order by sort
Output:
emp_id | emp_name
--------+---------------------------------
1 | President
2 | Vice President
3 | CEO
4 | CTO
5 | Group Project Manager
7 | Project Manager 2
14 | Team Leader 2
15 | Software Engineer 3
16 | Software Engineer 4
17 | Test Lead 2
19 | Tester 4
18 | Tester 3
20 | Tester 5
6 | Project Manager 1
8 | Team Leader 1
9 | Software Engineer 1
10 | Software Engineer 2
11 | Test Lead 1
12 | Tester 1
13 | Tester 2
(20 rows)
Project Manager 2 comes before Project Manager 1. Tester 4 comes before Tester 3
The technique lies in numeric text substitution for b.name if there's an order_override(non-null):
sort || ' : ' || coalesce( lpad(order_override::text, 10, '0'), b.emp_name )
Above code is Postgres, to convert to Sql Server, remove the word RECURSIVE
, change REPEAT
to REPLICATE
, ||
to +
.
Equivalent of...
lpad(order_override::text, 10, '0')
...is:
RIGHT( REPLICATE('0',10) + CONVERT(VARCHAR, order_override), 10)