问题
The initial motivation for this one, was to display Oracles' actual execution plans, saved in GV$SQL_PLAN, in a visual, clear way.
- I've attached my suggested solutions.
- Please feel free to add yours, as long as it fulfills the requirements.
- Please mention the database name your solution was written for.
Requirements
Input
- A table containing columns "id" (node's id) and "pid" (node's parent id).
Output
- The result should be a an ASCII art graph (see example below)
- Each pair of "id" and "pid" nodes should be connected with an edge.
- The root node might have an additional single edge.
- There should be no other edged especially not edges that are not connected to any node in one of their sides.
Code
- A single SELECT statement based only on native SQL
- No UDF (User Defined Functions).
- No T-SQL, PL/SQL etc.
Sample data
create table h (id int,pid int);
insert into h (id,pid) values (0 ,null);
insert into h (id,pid) values (1 ,0 );
insert into h (id,pid) values (2 ,1 );
insert into h (id,pid) values (3 ,2 );
insert into h (id,pid) values (4 ,3 );
insert into h (id,pid) values (5 ,4 );
insert into h (id,pid) values (6 ,3 );
insert into h (id,pid) values (7 ,6 );
insert into h (id,pid) values (8 ,7 );
insert into h (id,pid) values (9 ,8 );
insert into h (id,pid) values (10 ,9 );
insert into h (id,pid) values (11 ,10 );
insert into h (id,pid) values (12 ,9 );
insert into h (id,pid) values (13 ,12 );
insert into h (id,pid) values (14 ,8 );
insert into h (id,pid) values (15 ,6 );
insert into h (id,pid) values (16 ,15 );
insert into h (id,pid) values (17 ,6 );
insert into h (id,pid) values (18 ,17 );
insert into h (id,pid) values (19 ,17 );
insert into h (id,pid) values (20 ,3 );
insert into h (id,pid) values (21 ,20 );
insert into h (id,pid) values (22 ,21 );
insert into h (id,pid) values (23 ,22 );
insert into h (id,pid) values (24 ,21 );
Results
Vertical siblings
|
|____ 1
|
|____ 2
|
|____ 3
|
|____ 4
| |
| |____ 5
|
|____ 6
| |
| |____ 7
| | |
| | |____ 8
| | |
| | |____ 9
| | | |
| | | |____ 10
| | | | |
| | | | |____ 11
| | | |
| | | |____ 12
| | | |
| | | |____ 13
| | |
| | |____ 14
| |
| |____ 15
| | |
| | |____ 16
| |
| |____ 17
| |
| |____ 18
| |
| |____ 19
|
|____ 20
|
|____ 21
|
|____ 22
| |
| |____ 23
|
|____ 24
Horizontal siblings
|
|
|
0
|
|
|
|
|
1
|
|
|
|
|
2
|
|
|
|
|
3
|
|
---------------------------------------
| | |
| | |
4 6 20
| | |
| | |
| ------------------- |
| | | | |
| | | | |
5 7 15 17 21
| | | |
| | | |
| | ------ ------
| | | | | |
| | | | | |
8 16 18 19 22 24
| |
| |
-------- |
| | |
| | |
9 14 23
|
|
------
| |
| |
10 12
| |
| |
| |
| |
| |
11 13
回答1:
Oracle
Vertical siblings
with last_sibling (id)
as
(
select max (id)
from h
group by pid
)
,tree (id,pid,branch) as
(
select h.id
,h.pid
,cast ('' as varchar (4000))
from h
where h.pid is null
union all
select h.id
,h.pid
,t.branch || case when ls.id is not null then ' ' else '|' end || ' '
from tree t
left join last_sibling ls
on ls.id =
t.id
join h
on h.pid =
t.id
) search depth first by id set order_by_id
,vertical_space (n)
as
(
select 1
from dual
union all
select vs.n + 1
from vertical_space vs
where vs.n - 1 < 1
)
select t.branch || case vs.n when 1 then '|____' || ' ' || cast (t.id as varchar(10)) else '|' end
from tree t
cross join vertical_space vs
order by t.order_by_id
,vs.n desc
;
回答2:
Oracle
Horizontal siblings
with il_parameters (atomic_width) as
(
select 5
from dual
)
select decode
(
n.n
,1
,listagg
(
lpad (' ',x_lpad * il_parameters.atomic_width)
|| rpad (lpad (decode (h.n_within_parent,1,decode (h.n_desc_within_parent,1,'|','-'),'-'),h.width/2 * il_parameters.atomic_width + 1,decode (h.n_within_parent,1,' ','-')),h.width * il_parameters.atomic_width,decode (h.n_desc_within_parent,1,' ','-'))
) within group (order by h.id)
,2
,listagg
(
lpad (' ',x_lpad * il_parameters.atomic_width)
|| rpad (lpad (' ',h.width/2 * il_parameters.atomic_width,' ') || '|' ,h.width * il_parameters.atomic_width)
) within group (order by h.id)
,3
,listagg
(
lpad (' ',x_lpad * il_parameters.atomic_width)
|| rpad (lpad (' ',h.width/2 * il_parameters.atomic_width,' ') || '|' ,h.width * il_parameters.atomic_width)
) within group (order by h.id)
,4
,listagg
(
lpad (' ',x_lpad * il_parameters.atomic_width)
|| rpad (lpad (' ',h.width/2 * il_parameters.atomic_width,' ') || h.id,h.width * il_parameters.atomic_width)
) within group (order by h.id)
,5
,listagg
(
lpad (' ',x_lpad * il_parameters.atomic_width)
|| rpad (lpad (' ',h.width/2 * il_parameters.atomic_width,' ') || decode (h.isleaf,0,'|',' ') ,h.width * il_parameters.atomic_width)
) within group (order by h.id)
,6
,listagg
(
lpad (' ',x_lpad * il_parameters.atomic_width)
|| rpad (lpad (' ',h.width/2 * il_parameters.atomic_width,' ') || decode (h.isleaf,0,'|',' ') ,h.width * il_parameters.atomic_width)
) within group (order by h.id)
) graph
from (select h.id id
,h.width width
,h.y y
,h.isleaf isleaf
,h.n_within_parent n_within_parent
,h.n_desc_within_parent n_desc_within_parent
, sum (regexp_substr (heirarchy_x_within_parent,'\d+',1,n.n))
- lag (sum (regexp_substr (heirarchy_x_within_parent,'\d+',1,n.n)),1,0) over
(
partition by h.y
order by h.id
)
- lag (h.width,1,0) over
(
partition by h.y
order by h.id
) x_lpad
from (select h.id
,h.width
,h.n_within_parent
,h.n_desc_within_parent
,level y
,sys_connect_by_path (x_within_parent,',') heirarchy_x_within_parent
,connect_by_isleaf isleaf
from (select h.id id
,h.pid pid
,count (*) width
,nvl
(
sum (count (*)) over
(
partition by h.pid
order by h.id
rows between unbounded preceding
and 1 preceding
)
,0
) x_within_parent
,row_number () over
(
partition by h.pid
order by h.id
) n_within_parent
,row_number () over
(
partition by h.pid
order by h.id desc
) n_desc_within_parent
from (select connect_by_root h.id id
,connect_by_root h.pid pid
from h
where connect_by_isleaf = 1
connect by h.pid = prior h.id
) h
group by h.id
,h.pid
) h
connect by h.pid = prior h.id
start with h.pid is null
) h
join (select rownum n
from dual
connect by level <= 100
) n
on n.n <=
regexp_count (h.heirarchy_x_within_parent,',')
group by h.id
,h.width
,h.y
,h.isleaf
,h.n_within_parent
,h.n_desc_within_parent
) h
cross join il_parameters
cross join (select rownum n
from dual
connect by level <= 6
) n
group by h.y
,n.n
order by h.y
,n.n
;
回答3:
SQLite
Vertical siblings
with last_sibling (id)
as
(
select max (id)
from h
group by pid
)
,tree (id,branch,path)
as
(
select 1 as id
,'' as branch
,'001' as path
union all
select h.id
,t.branch || case when ls.id is not null then ' ' else '|' end || ' '
,t.path || '_' || substr ('00000' || h.id,-5)
from tree t
left join last_sibling ls
on ls.id =
t.id
join h
on h.pid =
t.id
)
,vertical_space (n)
as
(
select 1
union all
select vs.n + 1
from vertical_space vs
where vs.n < 2
)
select t.branch || case vs.n when 1 then '|____' || ' ' || cast (t.id as text) else '|' end
from tree t
cross join vertical_space vs
order by t.path
,vs.n desc
;
回答4:
SQL Server
Vertical siblings
with last_sibling (id)
as
(
select max (id)
from h
group by pid
)
,tree (id,branch,path)
as
(
select h.id
,cast ('' as varchar(max))
,cast (right (replicate ('0',10) + cast (h.id as varchar(10)),10) as varchar(max))
from h
where h.pid is null
union all
select h.id
,t.branch + case when (select 1 xxxfrom last_sibling ls where ls.id = t.id) = 1 then ' ' else '|' end + ' '
,t.path + '_' + right (replicate ('0',10) + cast (h.id as varchar(10)),10)
from tree t
join h
on h.pid =
t.id
)
,vertical_space (n)
as
(
select 1
union all
select vs.n + 1
from vertical_space vs
where vs.n < 2
)
select t.branch + case vs.n when 1 then '|____' + ' ' + cast (t.id as varchar(10)) else '|' end
from tree t
cross join vertical_space vs
order by t.path
,vs.n desc
option (maxrecursion 0)
;
回答5:
Teradata
Vertical siblings
with recursive tree (id,branch,path)
as
(
select 1 as id
,cast ('' as varchar (1000)) as branch
,cast ('001' as varchar (1000)) as path
from (select 'x') x(x)
union all
select h.id
,t.branch || case when ls.id is not null then ' ' else '|' end || ' '
,t.path || '_' || substr ('00000' || h.id,-5)
from tree t
left join last_sibling ls
on ls.id =
t.id
join h
on h.pid =
t.id
)
,last_sibling (id)
as
(
select max (id)
from h
group by pid
)
,recursive vertical_space (n)
as
(
select 1
from (select 'x') x(x)
union all
select vs.n + 1
from vertical_space vs
where vs.n < 2
)
select t.branch || case vs.n when 1 then '|____' || ' ' || cast (t.id as varchar(10)) else '|' end
from tree t
cross join vertical_space vs
order by t.path
,vs.n desc
;
来源:https://stackoverflow.com/questions/39925938/sql-challenge-puzzle-how-to-create-an-ascii-art-hierarchy-tree-with-an-sql-quer