在Oracle 中我们知道有一个 Hierarchical Queries 通过CONNECT BY 我们可以方便的查了所有当前节点下的所有子节点。但很遗憾,在MySQL的目前版本中还没有对应的功能。

在MySQL中如果是有限的层次,比如我们事先如果可以确定这个树的最大深度是4, 那么所有节点为根的树的深度均不会超过4,则我们可以直接通过left join 来实现。

但很多时候我们无法控制树的深度。这时就需要在MySQL中用存储过程来实现或在你的程序中来实现这个递归。本文讨论一下几种实现的方法。

样例数据:

mysql> create table treeNodes
    -> (
    ->  id int primary key,
    ->  nodename varchar(20),
    ->  pid int
    -> );
Query OK, 0 rows affected (0.09 sec) 
mysql> select * from treenodes;
+----+----------+------+
| id | nodename | pid  |
+----+----------+------+
|  1 | A        |    0 |
|  2 | B        |    1 |
|  3 | C        |    1 |
|  4 | D        |    2 |
|  5 | E        |    2 |
|  6 | F        |    3 |
|  7 | G        |    6 |
|  8 | H        |    0 |
|  9 | I        |    8 |
| 10 | J        |    8 |
| 11 | K        |    8 |
| 12 | L        |    9 |
| 13 | M        |    9 |
| 14 | N        |   12 |
| 15 | O        |   12 |
| 16 | P        |   15 |
| 17 | Q        |   15 |
+----+----------+------+
17 rows in set (0.00 sec)

树形图如下

1:A
  +-- 2:B
  |    +-- 4:D
  |    +-- 5:E
  +-- 3:C
       +-- 6:F
            +-- 7:G
 8:H
  +-- 9:I
  |    +-- 12:L
  |    |    +--14:N
  |    |    +--15:O
  |    |        +--16:P
  |    |        +--17:Q
  |    +-- 13:M
  +-- 10:J
  +-- 11:K

方法一:利用函数来得到所有子节点号。

创建一个function getChildLst, 得到一个由所有子节点号组成的字符串.

mysql> delimiter //
mysql>
mysql> CREATE FUNCTION `getChildLst`(rootId INT)
    -> RETURNS varchar(1000)
    -> BEGIN
    ->   DECLARE sTemp VARCHAR(1000);
    ->   DECLARE sTempChd VARCHAR(1000);
    ->
    ->   SET sTemp = '$';
    ->   SET sTempChd =cast(rootId as CHAR);
    ->
    ->   WHILE sTempChd is not null DO
    ->     SET sTemp = concat(sTemp,',',sTempChd);
    ->     SELECT group_concat(id) INTO sTempChd FROM treeNodes where FIND_IN_SET(pid,sTempChd)>0;
    ->   END WHILE;
    ->   RETURN sTemp;
    -> END
    -> //
Query OK, 0 rows affected (0.00 sec)

mysql>
mysql> delimiter ;

使用我们直接利用find_in_set函数配合这个getChildlst来查找

mysql> select getChildLst(1);
+-----------------+
| getChildLst(1)  |
+-----------------+
| $,1,2,3,4,5,6,7 |
+-----------------+
1 row in set (0.00 sec)

mysql> select * from treeNodes
    -> where FIND_IN_SET(id, getChildLst(1));
+----+----------+------+
| id | nodename | pid  |
+----+----------+------+
|  1 | A        |    0 |
|  2 | B        |    1 |
|  3 | C        |    1 |
|  4 | D        |    2 |
|  5 | E        |    2 |
|  6 | F        |    3 |
|  7 | G        |    6 |
+----+----------+------+
7 rows in set (0.01 sec)

mysql> select * from treeNodes
    -> where FIND_IN_SET(id, getChildLst(3));
+----+----------+------+
| id | nodename | pid  |
+----+----------+------+
|  3 | C        |    1 |
|  6 | F        |    3 |
|  7 | G        |    6 |
+----+----------+------+
3 rows in set (0.01 sec)

优点: 简单,方便,没有递归调用层次深度的限制 (max_sp_recursion_depth,最大255) ;

缺点:长度受限,虽然可以扩大 RETURNS varchar(1000),但总是有最大限制的。

MySQL目前版本( 5.1.33-community)中还不支持function 的递归调用。

方法二:利用临时表和过程递归

创建存储过程如下。createChildLst 为递归过程,showChildLst为调用入口过程,准备临时表及初始化。

mysql> delimiter //
mysql>
mysql> # 入口过程
mysql> CREATE PROCEDURE showChildLst (IN rootId INT)
    -> BEGIN
    ->  CREATE TEMPORARY TABLE IF NOT EXISTS tmpLst 
    ->   (sno int primary key auto_increment,id int,depth int);
    ->  DELETE FROM tmpLst;
    ->
    ->  CALL createChildLst(rootId,0);
    ->
    ->  select tmpLst.*,treeNodes.* from tmpLst,treeNodes where tmpLst.id=treeNodes.id order by tmpLst.sno;
    -> END;
    -> //
Query OK, 0 rows affected (0.00 sec)

mysql>
mysql> # 递归过程
mysql> CREATE PROCEDURE createChildLst (IN rootId INT,IN nDepth INT)
    -> BEGIN
    ->  DECLARE done INT DEFAULT 0;
    ->  DECLARE b INT;
    ->  DECLARE cur1 CURSOR FOR SELECT id FROM treeNodes where pid=rootId;
    ->  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
    ->
    ->  insert into tmpLst values (null,rootId,nDepth);

->
    ->  OPEN cur1;
    ->
    ->  FETCH cur1 INTO b;
    ->  WHILE done=0 DO
    ->          CALL createChildLst(b,nDepth+1);
    ->          FETCH cur1 INTO b;
    ->  END WHILE;
    ->
    ->  CLOSE cur1;
    -> END;
    -> //
Query OK, 0 rows affected (0.00 sec) 
mysql> delimiter ;

调用时传入结点

mysql> call showChildLst(1);
+-----+------+-------+----+----------+------+
| sno | id   | depth | id | nodename | pid  |
+-----+------+-------+----+----------+------+
|   4 |    1 |     0 |  1 | A        |    0 |
|   5 |    2 |     1 |  2 | B        |    1 |
|   6 |    4 |     2 |  4 | D        |    2 |
|   7 |    5 |     2 |  5 | E        |    2 |
|   8 |    3 |     1 |  3 | C        |    1 |
|   9 |    6 |     2 |  6 | F        |    3 |
|  10 |    7 |     3 |  7 | G        |    6 |
+-----+------+-------+----+----------+------+

7 rows in set (0.13 sec)

Query OK, 0 rows affected, 1 warning (0.14 sec)

mysql>
mysql> call showChildLst(3);
+-----+------+-------+----+----------+------+
| sno | id   | depth | id | nodename | pid  |
+-----+------+-------+----+----------+------+
|   1 |    3 |     0 |  3 | C        |    1 |
|   2 |    6 |     1 |  6 | F        |    3 |
|   3 |    7 |     2 |  7 | G        |    6 |
+-----+------+-------+----+----------+------+

3 rows in set (0.11 sec)

Query OK, 0 rows affected, 1 warning (0.11 sec)

depth 为深度,这样可以在程序进行一些显示上的格式化处理。类似于oracle中的 level 伪列。sno 仅供排序控制。这样你还可以通过临时表tmpLst与数据库中其它表进行联接查询。

MySQL中你可以利用系统参数 max_sp_recursion_depth 来控制递归调用的层数上限。如下例设为12.

mysql> set max_sp_recursion_depth=12;
Query OK, 0 rows affected (0.00 sec)

优点 : 可以更灵活处理,及层数的显示。并且可以按照树的遍历顺序得到结果。

缺点 : 递归有255的限制。

方法三:利用中间表和过程

(本方法由yongyupost2000提供样子改编)

创建存储过程如下。由于MySQL中不允许在同一语句中对临时表多次引用,只以使用普通表tmpLst来实现了。当然你的程序中负责在用完后清除这个表。

delimiter //

drop PROCEDURE IF EXISTS  showTreeNodes_yongyupost2000//

CREATE PROCEDURE showTreeNodes_yongyupost2000 (IN rootid INT)
BEGIN
 DECLARE Level int ;
 drop TABLE IF EXISTS tmpLst;
 CREATE TABLE tmpLst (
  id int,
  nLevel int,
  sCort varchar(8000)
 );
 
 Set Level=0 ;
 INSERT into tmpLst SELECT id,Level,ID FROM treeNodes WHERE PID=rootid;
 WHILE ROW_COUNT()>0 DO
  SET Level=Level+1 ;
  INSERT into tmpLst 
   SELECT A.ID,Level,concat(B.sCort,A.ID) FROM treeNodes A,tmpLst B 
    WHERE  A.PID=B.ID AND B.nLevel=Level-1  ;
 END WHILE;
  
END;
//

delimiter ;

CALL showTreeNodes_yongyupost2000(0);

执行完后会产生一个tmpLst表,nLevel 为节点深度,sCort 为排序字段。
使用方法

SELECT concat(SPACE(B.nLevel*2),'+--',A.nodename)
FROM treeNodes A,tmpLst B 
WHERE A.ID=B.ID 
ORDER BY B.sCort;

+--------------------------------------------+
| concat(SPACE(B.nLevel*2),'+--',A.nodename) |
+--------------------------------------------+
| +--A                                       |
|   +--B                                     |
|     +--D                                   |
|     +--E                                   |
|   +--C                                     |
|     +--F                                   |
|       +--G                                 |
| +--H                                       |
|   +--J                                     |
|   +--K                                     |
|   +--I                                     |
|     +--L                                   |
|       +--N                                 |
|       +--O                                 |
|         +--P                               |
|         +--Q                               |
|     +--M                                   |
+--------------------------------------------+
17 rows in set (0.00 sec)

优点 : 层数的显示。并且可以按照树的遍历顺序得到结果。没有递归限制。
缺点 : MySQL中对临时表的限制,只能使用普通表,需做事后清理。

以上是几个在MySQL中用存储过程比较简单的实现方法。

MySQL中进行树状所有子节点的查询的更多相关文章

  1. MySQL中进行树状所有子节点的查询 . mysql根据父id 查询所有的子id

    在Oracle 中我们知道有一个 Hierarchical Queries 通过CONNECT BY 我们可以方便的查了所有当前节点下的所有子节点.但很遗憾,在MySQL的目前版本中还没有对应的功能. ...

  2. MySQL实现树状所有子节点查询的方法

    本文实例讲述了MySQL实现树状所有子节点查询的方法.分享给大家供大家参考,具体如下: 在Oracle 中我们知道有一个 Hierarchical Queries 通过CONNECT BY 我们可以方 ...

  3. mysql中递归树状结构<转>

    在Oracle 中我们知道有一个 Hierarchical Queries 通过CONNECT BY 我们可以方便的查了所有当前节点下的所有子节点.但很遗憾,在MySQL的目前版本中还没有对应的功能. ...

  4. 在MySQL中如何使用覆盖索引优化limit分页查询

    背景 今年3月份时候,线上发生一次大事故.公司主要后端服务器发生宕机,所有接口超时.宕机半小时后,又自动恢复正常.但是过了2小时,又再次发生宕机. 通过接口日志,发现MySQL数据库无法响应服务器.在 ...

  5. 在Winform界面中使用DevExpress的TreeList实现节点过滤查询的两种方式

    在我较早的一篇随笔<在DevExpress程序中使用TeeList控件以及节点查询的处理>中,介绍了在树形列表TreeList控件上面,利用SearchControl实现节点的模糊查询过滤 ...

  6. 利用id来进行树状数组,而不是离散化以后的val HDU 4417 离线+树状数组

    题目大意:给你一个长度为n的数组,问[L,R]之间<=val的个数 思路:就像标题说的那样就行了.树状数组不一定是离散化以后的区间,而可以是id //看看会不会爆int!数组会不会少了一维! / ...

  7. mysql中利用show profile很直观的看到查询缓存的作用。

    1.首先,开启mysql的查询缓存. 查看查询缓存情况: MariaDB [test]> show variables like '%query_cache%';+--------------- ...

  8. MySQL 树形结构 根据指定节点 获取其下属的所有子节点(包含路径上的枝干节点和叶子节点)

    背景说明 需求:MySQL树形结构, 根据指定的节点,获取其下属的所有子节点(包含路径上的枝干节点和叶子节点) 枝干节点:如果一个节点下还有子节点,则为枝干节点. 叶子节点:如果一个节点下不再有子节点 ...

  9. mysql 递归查找菜单节点的所有子节点

    背景                                                                                                   ...

随机推荐

  1. centos 安装mysql 登录进提示 Access denied for user 'root'@'localhost' (using password: NO)

    # service mysqld stop # mysqld_safe --user=mysql --skip-grant-tables --skip-networking & # mysql ...

  2. J2EE事务

    一.J2EE 事务处理方式 1. 本地事务:紧密依赖于底层资源管理器(例如数据库连接 ),事务处理局限在当前事务资源内.此种事务处理方式不存在对应用服务器的依赖,因而部署灵活却无法支持多数据源的分布式 ...

  3. Yii 框架创建自己的 web 应用

    http://m.baidu.com/from=2001a/bd_page_type=1/ssid=0/uid=0/pu=usm%400%2Csz%401320_1003%2Cta%40iphone_ ...

  4. FreeMarker笔记 第二章 数值和类型

    2.1 基本内容 2.1.1 简介 2.1.2 什么是数值 和程序语言中的数值类型是相似的. 2.1.3 什么是类型? 2.1.4 数据模型是哈希表 2.2 类型 2.2.1 简介 2.2.2 标量 ...

  5. .Net 学习

    .Net 的微型Web框架Nancy ORM工具 Simple Data Ojbective-C 与 swift Xamarin for VisualStudio jQuery 1. 绝对的万金油,核 ...

  6. java webservice的多种实现方法汇总

    一.基于EJB容器管理webservice :     1.首先建立一个Web services EndPoint: package cn.test.service.impl; import java ...

  7. tableView 显示区域偏移

    在SB拖了一个tableView , 在显示的时候显示区域和tableView的区域不一致, (UITableViewWrapperView 和 UITableView frame不一致) 在SB上看 ...

  8. Linux入门视频

    为了方便新手学习Linux,本人专门录制了以下Linux初级视频方便学习,本系列多媒体教程已完成的博文: 轻松学习Linux之入门篇 http://chenguang.blog.51cto.com/3 ...

  9. 【360开源】thinkjs:基于Promise的Node.js MVC框架 (转)

    thinkjs是360奇舞团开源的一款Node.js MVC框架,该框架底层基于Promise来实现,很好的解决了Node.js里异步回调的问题.360奇舞团(奇虎75Team),是奇虎360公司We ...

  10. permission denied部署django 遇到没有python_egg_cache的问题解决

    检查/etc/httpd/logs/error_log,看是否有如下错误: [Errno 13] Permission denied: '/var/www/.python-eggs' 这时候需要编辑“ ...