Note: The test database version is MySQL 8.0
If you need scott users to create tables and enter data statements, please refer to:
scott create tables and enter data sql scripts
1. Demand
Return a result set, which describes the hierarchy of the entire table.
In the EMP table, employee KING has no manager, so KING is the root node.
Starting from KING, all employees under KING and all employees under KING (if any) are displayed.
最后,返回下列结果集:
±-----------------------------+
| emp_tree |
±-----------------------------+
| KING |
| KING - BLAKE |
| KING - BLAKE - ALLEN |
| KING - BLAKE - JAMES |
| KING - BLAKE - MARTIN |
| KING - BLAKE - TURNER |
| KING - BLAKE - WARD |
| KING - CLARK |
| KING - CLARK - MILLER |
| KING - JONES |
| KING - JONES - FORD |
| KING - JONES - FORD - SMITH |
| KING - JONES - SCOTT |
| KING - JONES - SCOTT - ADAMS |
±-----------------------------+
2. Solution
2.1 Use union and multiple self-joins
select emp_tree
from (
select ename as emp_tree
from emp
where mgr is null
union
select concat(a.ename,' - ',b.ename)
from emp a
inner join emp b
on a.empno = b.mgr
where a.mgr is null
union
select concat(a.ename,' - ',
b.ename,' - ',c.ename)
from emp a
inner join emp b
on a.empno = b.mgr
left join emp c
on b.empno = c.mgr
where a.ename = 'KING'
union
select concat(a.ename,' - ',b.ename,' - ',
c.ename,' - ',d.ename)
from emp a
inner join emp b
on a.emono = b.mgr
inner join emp c
on b.emono = c.mgr
left join emp d
on c.empno = d.mgr
where a.ename = 'KING'
) x
where tree is not null
order by 1;
Test Record:
mysql> select emp_tree
-> from (
-> select ename as emp_tree
-> from emp
-> where mgr is null
-> union
-> select concat(a.ename,' - ',b.ename)
-> from emp a
-> inner join emp b
-> on a.empno = b.mgr
-> where a.mgr is null
-> union
-> select concat(a.ename,' - ',
-> b.ename,' - ',c.ename)
-> from emp a
-> inner join emp b
-> on a.empno = b.mgr
-> left join emp c
-> on b.empno = c.mgr
-> where a.ename = 'KING'
-> union
-> select concat(a.ename,' - ',b.ename,' - ',
-> c.ename,' - ',d.ename)
-> from emp a
-> inner join emp b
-> on a.empno = b.mgr
-> inner join emp c
-> on b.empno = c.mgr
-> left join emp d
-> on c.empno = d.mgr
-> where a.ename = 'KING'
-> ) x
-> where emp_tree is not null
-> order by 1;
+------------------------------+
| emp_tree |
+------------------------------+
| KING |
| KING - BLAKE |
| KING - BLAKE - ALLEN |
| KING - BLAKE - JAMES |
| KING - BLAKE - MARTIN |
| KING - BLAKE - TURNER |
| KING - BLAKE - WARD |
| KING - CLARK |
| KING - CLARK - MILLER |
| KING - JONES |
| KING - JONES - FORD |
| KING - JONES - FORD - SMITH |
| KING - JONES - SCOTT |
| KING - JONES - SCOTT - ADAMS |
+------------------------------+
14 rows in set (0.00 sec)
2.2 with recursive method
You can see that with recursion, which MySQL 8.0 starts to support, can make the code much easier and the code logic looks more hierarchical.
with recursive emp2(ename,empno) AS
(
SELECT cast(ename as char(200)) ename,empno
from emp
where mgr is null
union ALL
SELECT concat(e2.ename,' - ',e1.ename) ,e1.empno
from emp e1,emp2 e2
where e1.mgr = e2.empno
)
select trim(ename) as ename
from emp2
Test Record
mysql> with recursive emp2(ename,empno) AS
-> (
-> SELECT cast(ename as char(200)) ename,empno
-> from emp
-> where mgr is null
-> union ALL
-> SELECT concat(e2.ename,' - ',e1.ename) ,e1.empno
-> from emp e1,emp2 e2
-> where e1.mgr = e2.empno
-> )
-> select trim(ename) as ename
-> from emp2;
+------------------------------+
| ename |
+------------------------------+
| KING |
| KING - JONES |
| KING - BLAKE |
| KING - CLARK |
| KING - JONES - SCOTT |
| KING - JONES - FORD |
| KING - BLAKE - ALLEN |
| KING - BLAKE - WARD |
| KING - BLAKE - MARTIN |
| KING - BLAKE - TURNER |
| KING - BLAKE - JAMES |
| KING - CLARK - MILLER |
| KING - JONES - SCOTT - ADAMS |
| KING - JONES - FORD - SMITH |
+------------------------------+
14 rows in set (0.00 sec)