使用MySQL 8的递归CTE遍历树

Posted wzy0623

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用MySQL 8的递归CTE遍历树相关的知识,希望对你有一定的参考价值。

目录

1. 从根遍历到叶

2. 从叶遍历到根

3. 确定叶子节点、分支节点和根节点

(1)使用相关子查询

(2)更高效的写法(一次外连接)


表数据:

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遍历树的主要内容,如果未能解决你的问题,请参考以下文章

SQL CTE树递归查询

MYSQL 8.019 CTE 递归查询怎么解决死循环三种方法

使用MySQL 8.0递归CTE查找层次结构表中的直接后代并传播给父级

mysql递归查询cte

CTE递归获取树层次结构

通过 PHP 使用 mysql 进行递归树遍历