使用MySQL 8的递归CTE遍历树
Posted wzy0623
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用MySQL 8的递归CTE遍历树相关的知识,希望对你有一定的参考价值。
目录
表数据:
mysql> select * from t1;
+------+------+
| id | pid |
+------+------+
| 7788 | 7566 |
| 7902 | 7566 |
| 7499 | 7698 |
| 7521 | 7698 |
| 7900 | 7698 |
| 7844 | 7698 |
| 7654 | 7698 |
| 7934 | 7782 |
| 7876 | 7788 |
| 7566 | 7839 |
| 7782 | 7839 |
| 7698 | 7839 |
| 7369 | 7902 |
| 7839 | NULL |
+------+------+
14 rows in set (0.00 sec)
1. 从根遍历到叶
mysql> with recursive x (sid,id,pid)
-> as (
-> select cast(id as char(100)), id,pid
-> from t1
-> where pid is null
-> union all
-> select concat(x.sid,'-->',e.id), e.id,e.pid
-> from t1 e, x
-> where x.id = e.pid
-> )
-> select sid
-> from x order by 1;
+---------------------------+
| sid |
+---------------------------+
| 7839 |
| 7839-->7566 |
| 7839-->7566-->7788 |
| 7839-->7566-->7788-->7876 |
| 7839-->7566-->7902 |
| 7839-->7566-->7902-->7369 |
| 7839-->7698 |
| 7839-->7698-->7499 |
| 7839-->7698-->7521 |
| 7839-->7698-->7654 |
| 7839-->7698-->7844 |
| 7839-->7698-->7900 |
| 7839-->7782 |
| 7839-->7782-->7934 |
+---------------------------+
14 rows in set (0.00 sec)
另一种显示(缩进):
mysql> with recursive x (sid,id,pid,ss,level)
-> as (
-> select cast(id as char(100)), id,pid,concat('..',id),1
-> from t1
-> where pid is null
-> union all
-> select concat(x.sid,'-->',e.id), e.id,e.pid,concat(lpad('.',2*(level+1),'.'),e.id),x.level + 1
-> from t1 e, x
-> where x.id = e.pid
-> )
-> select ss
-> from x order by sid;
+--------------+
| ss |
+--------------+
| ..7839 |
| ....7566 |
| ......7788 |
| ........7876 |
| ......7902 |
| ........7369 |
| ....7698 |
| ......7499 |
| ......7521 |
| ......7654 |
| ......7844 |
| ......7900 |
| ....7782 |
| ......7934 |
+--------------+
14 rows in set (0.00 sec)
2. 从叶遍历到根
mysql> with recursive x (sid,id,pid)
-> as (
-> select cast(id as char(100)), id,pid
-> from t1
-> -- where pid is null
-> union all
-> select concat(x.sid,'-->',e.id), e.id,e.pid
-> from t1 e, x
-> where x.pid = e.id
-> )
-> select sid
-> from x where pid is null order by 1;
+---------------------------+
| sid |
+---------------------------+
| 7369-->7902-->7566-->7839 |
| 7499-->7698-->7839 |
| 7521-->7698-->7839 |
| 7566-->7839 |
| 7654-->7698-->7839 |
| 7698-->7839 |
| 7782-->7839 |
| 7788-->7566-->7839 |
| 7839 |
| 7844-->7698-->7839 |
| 7876-->7788-->7566-->7839 |
| 7900-->7698-->7839 |
| 7902-->7566-->7839 |
| 7934-->7782-->7839 |
+---------------------------+
14 rows in set (0.00 sec)
3. 确定叶子节点、分支节点和根节点
(1)使用相关子查询
mysql> select id,
-> (select 1 - sign(count(*)) from t1 d
-> -- 有子节点
-> where d.pid = e.id) as is_leaf,
-> (select sign(count(*)) from t1 d
-> -- 有子节点并且有父节点
-> where d.pid = e.id and e.pid is not null) as is_branch,
-> (select sign(count(*)) from t1 d
-> -- 没有父节点
-> where d.id = e.id and d.pid is null) as is_root
-> from t1 e
-> order by 4 desc,3 desc,id;
+------+---------+-----------+---------+
| id | is_leaf | is_branch | is_root |
+------+---------+-----------+---------+
| 7839 | 0 | 0 | 1 |
| 7566 | 0 | 1 | 0 |
| 7698 | 0 | 1 | 0 |
| 7782 | 0 | 1 | 0 |
| 7788 | 0 | 1 | 0 |
| 7902 | 0 | 1 | 0 |
| 7369 | 1 | 0 | 0 |
| 7499 | 1 | 0 | 0 |
| 7521 | 1 | 0 | 0 |
| 7654 | 1 | 0 | 0 |
| 7844 | 1 | 0 | 0 |
| 7876 | 1 | 0 | 0 |
| 7900 | 1 | 0 | 0 |
| 7934 | 1 | 0 | 0 |
+------+---------+-----------+---------+
14 rows in set (0.00 sec)
(2)更高效的写法(一次外连接)
mysql> select distinct id,
-> case when flag = 'is_leaf' then 1 else 0 end is_leaf,
-> case when flag = 'is_branch' then 1 else 0 end is_branch,
-> case when flag = 'is_root' then 1 else 0 end is_root
-> from (select t2.id,
-> case when t1.id is null then 'is_leaf'
-> when t2.pid is null then 'is_root'
-> else 'is_branch' end flag
-> from t1 right join t1 t2 on t1.pid=t2.id) t
-> order by 4 desc,3 desc,id;
+------+---------+-----------+---------+
| id | is_leaf | is_branch | is_root |
+------+---------+-----------+---------+
| 7839 | 0 | 0 | 1 |
| 7566 | 0 | 1 | 0 |
| 7698 | 0 | 1 | 0 |
| 7782 | 0 | 1 | 0 |
| 7788 | 0 | 1 | 0 |
| 7902 | 0 | 1 | 0 |
| 7369 | 1 | 0 | 0 |
| 7499 | 1 | 0 | 0 |
| 7521 | 1 | 0 | 0 |
| 7654 | 1 | 0 | 0 |
| 7844 | 1 | 0 | 0 |
| 7876 | 1 | 0 | 0 |
| 7900 | 1 | 0 | 0 |
| 7934 | 1 | 0 | 0 |
+------+---------+-----------+---------+
14 rows in set (0.00 sec)
以上是关于使用MySQL 8的递归CTE遍历树的主要内容,如果未能解决你的问题,请参考以下文章
MYSQL 8.019 CTE 递归查询怎么解决死循环三种方法