- 在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中递归树状结构
最新推荐文章于 2024-08-19 16:10:28 发布