mysql中递归树状结构


  1. 在Oracle 中我们知道有一个 Hierarchical Queries 通过CONNECT BY 我们可以方便的查了所有当前节点下的所有子节点。但很遗憾,在MySQL的目前版本中还没有对应的功能。  
  2.   
  3.    
  4.   
  5. 在MySQL中如果是有限的层次,比如我们事先如果可以确定这个树的最大深度是4, 那么所有节点为根的树的深度均不会超过4,则我们可以直接通过left join 来实现。  
  6.   
  7.    
  8.   
  9. 但很多时候我们无法控制树的深度。这时就需要在MySQL中用存储过程来实现或在你的程序中来实现这个递归。本文讨论一下几种实现的方法。  
  10.   
  11.    
  12.   
  13. 样例数据:  
  14.   
  15.   
  16. mysql> create table treeNodes  
  17.     -> (  
  18.     ->  id int primary key,  
  19.     ->  nodename varchar(20),  
  20.     ->  pid int  
  21.     -> );  
  22. Query OK, 0 rows affected (0.09 sec)   
  23. mysql> select * from treenodes;  
  24. +----+----------+------+  
  25. | id | nodename | pid  |  
  26. +----+----------+------+  
  27. |  1 | A        |    0 |  
  28. |  2 | B        |    1 |  
  29. |  3 | C        |    1 |  
  30. |  4 | D        |    2 |  
  31. |  5 | E        |    2 |  
  32. |  6 | F        |    3 |  
  33. |  7 | G        |    6 |  
  34. |  8 | H        |    0 |  
  35. |  9 | I        |    8 |  
  36. 10 | J        |    8 |  
  37. 11 | K        |    8 |  
  38. 12 | L        |    9 |  
  39. 13 | M        |    9 |  
  40. 14 | N        |   12 |  
  41. 15 | O        |   12 |  
  42. 16 | P        |   15 |  
  43. 17 | Q        |   15 |  
  44. +----+----------+------+  
  45. 17 rows in set (0.00 sec)  
  46.   
  47. 树形图如下  
  48.   
  49.   
  50.  1:A  
  51.   +-- 2:B  
  52.   |    +-- 4:D  
  53.   |    +-- 5:E  
  54.   +-- 3:C  
  55.        +-- 6:F  
  56.             +-- 7:G  
  57.  8:H  
  58.   +-- 9:I  
  59.   |    +-- 12:L  
  60.   |    |    +--14:N  
  61.   |    |    +--15:O  
  62.   |    |        +--16:P  
  63.   |    |        +--17:Q  
  64.   |    +-- 13:M  
  65.   +-- 10:J  
  66.   +-- 11:K     
  67.   
  68.   
  69.    
  70.   
  71. 方法一:利用函数来得到所有子节点号。  
  72.   
  73. 创建一个function getChildLst, 得到一个由所有子节点号组成的字符串.    
  74.   
  75.   
  76. mysql> delimiter //  
  77. mysql>  
  78. mysql> CREATE FUNCTION `getChildLst`(rootId INT)  
  79.     -> RETURNS varchar(1000)  
  80.     -> BEGIN  
  81.     ->   DECLARE sTemp VARCHAR(1000);  
  82.     ->   DECLARE sTempChd VARCHAR(1000);  
  83.     ->  
  84.     ->   SET sTemp = '$';  
  85.     ->   SET sTempChd =cast(rootId as CHAR);  
  86.     ->  
  87.     ->   WHILE sTempChd is not null DO  
  88.     ->     SET sTemp = concat(sTemp,',',sTempChd);  
  89.     ->     SELECT group_concat(id) INTO sTempChd FROM treeNodes where FIND_IN_SET(pid,sTempChd)>0;  
  90.     ->   END WHILE;  
  91.     ->   RETURN sTemp;  
  92.     -> END  
  93.     -> //  
  94. Query OK, 0 rows affected (0.00 sec)  
  95.   
  96. mysql>  
  97. mysql> delimiter ;  
  98.   
  99.   
  100. 使用我们直接利用find_in_set函数配合这个getChildlst来查找  
  101.   
  102.   
  103. mysql> select getChildLst(1);  
  104. +-----------------+  
  105. | getChildLst(1)  |  
  106. +-----------------+  
  107. | $,1,2,3,4,5,6,7 |  
  108. +-----------------+  
  109. 1 row in set (0.00 sec)   
  110.   
  111. mysql> select * from treeNodes  
  112.     -> where FIND_IN_SET(id, getChildLst(1));  
  113. +----+----------+------+  
  114. | id | nodename | pid  |  
  115. +----+----------+------+  
  116. |  1 | A        |    0 |  
  117. |  2 | B        |    1 |  
  118. |  3 | C        |    1 |  
  119. |  4 | D        |    2 |  
  120. |  5 | E        |    2 |  
  121. |  6 | F        |    3 |  
  122. |  7 | G        |    6 |  
  123. +----+----------+------+  
  124. 7 rows in set (0.01 sec)  
  125.   
  126. mysql> select * from treeNodes  
  127.     -> where FIND_IN_SET(id, getChildLst(3));  
  128. +----+----------+------+  
  129. | id | nodename | pid  |  
  130. +----+----------+------+  
  131. |  3 | C        |    1 |  
  132. |  6 | F        |    3 |  
  133. |  7 | G        |    6 |  
  134. +----+----------+------+  
  135. 3 rows in set (0.01 sec)  
  136.   
  137.    
  138.   
  139. 优点: 简单,方便,没有递归调用层次深度的限制 (max_sp_recursion_depth,最大255) ;  
  140.   
  141. 缺点:长度受限,虽然可以扩大 RETURNS varchar(1000),但总是有最大限制的。  
  142.   
  143. MySQL目前版本( 5.1.33-community)中还不支持function 的递归调用。  
  144.   
  145.    
  146.   
  147. 方法二:利用临时表和过程递归  
  148.   
  149. 创建存储过程如下。createChildLst 为递归过程,showChildLst为调用入口过程,准备临时表及初始化。  
  150.   
  151.   
  152.   
  153. mysql> delimiter //  
  154. mysql>  
  155. mysql> # 入口过程  
  156. mysql> CREATE PROCEDURE showChildLst (IN rootId INT)  
  157.     -> BEGIN  
  158.     ->  CREATE TEMPORARY TABLE IF NOT EXISTS tmpLst   
  159.     ->   (sno int primary key auto_increment,id int,depth int);  
  160.     ->  DELETE FROM tmpLst;  
  161.     ->  
  162.     ->  CALL createChildLst(rootId,0);  
  163.     ->  
  164.     ->  select tmpLst.*,treeNodes.* from tmpLst,treeNodes where tmpLst.id=treeNodes.id order by tmpLst.sno;  
  165.     -> END;  
  166.     -> //  
  167. Query OK, 0 rows affected (0.00 sec)  
  168.   
  169. mysql>  
  170. mysql> # 递归过程  
  171. mysql> CREATE PROCEDURE createChildLst (IN rootId INT,IN nDepth INT)  
  172.     -> BEGIN  
  173.     ->  DECLARE done INT DEFAULT 0;  
  174.     ->  DECLARE b INT;  
  175.     ->  DECLARE cur1 CURSOR FOR SELECT id FROM treeNodes where pid=rootId;  
  176.     ->  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;  
  177.     ->  
  178.     ->  insert into tmpLst values (null,rootId,nDepth);  
  179.   
  180.     ->  
  181.     ->  OPEN cur1;  
  182.     ->  
  183.     ->  FETCH cur1 INTO b;  
  184.     ->  WHILE done=0 DO  
  185.     ->          CALL createChildLst(b,nDepth+1);  
  186.     ->          FETCH cur1 INTO b;  
  187.     ->  END WHILE;  
  188.     ->  
  189.     ->  CLOSE cur1;  
  190.     -> END;  
  191.     -> //  
  192. Query OK, 0 rows affected (0.00 sec)   
  193. mysql> delimiter ;  
  194.   
  195.   
  196. 调用时传入结点  
  197.   
  198.   
  199. mysql> call showChildLst(1);  
  200. +-----+------+-------+----+----------+------+  
  201. | sno | id   | depth | id | nodename | pid  |  
  202. +-----+------+-------+----+----------+------+  
  203. |   4 |    1 |     0 |  1 | A        |    0 |  
  204. |   5 |    2 |     1 |  2 | B        |    1 |  
  205. |   6 |    4 |     2 |  4 | D        |    2 |  
  206. |   7 |    5 |     2 |  5 | E        |    2 |  
  207. |   8 |    3 |     1 |  3 | C        |    1 |  
  208. |   9 |    6 |     2 |  6 | F        |    3 |  
  209. |  10 |    7 |     3 |  7 | G        |    6 |  
  210. +-----+------+-------+----+----------+------+  
  211.   
  212. 7 rows in set (0.13 sec)  
  213.   
  214. Query OK, 0 rows affected, 1 warning (0.14 sec)  
  215.   
  216. mysql>  
  217. mysql> call showChildLst(3);  
  218. +-----+------+-------+----+----------+------+  
  219. | sno | id   | depth | id | nodename | pid  |  
  220. +-----+------+-------+----+----------+------+  
  221. |   1 |    3 |     0 |  3 | C        |    1 |  
  222. |   2 |    6 |     1 |  6 | F        |    3 |  
  223. |   3 |    7 |     2 |  7 | G        |    6 |  
  224. +-----+------+-------+----+----------+------+  
  225.   
  226. 3 rows in set (0.11 sec)  
  227.   
  228. Query OK, 0 rows affected, 1 warning (0.11 sec)  
  229.   
  230. depth 为深度,这样可以在程序进行一些显示上的格式化处理。类似于oracle中的 level 伪列。sno 仅供排序控制。这样你还可以通过临时表tmpLst与数据库中其它表进行联接查询。  
  231.   
  232.    
  233.   
  234. MySQL中你可以利用系统参数 max_sp_recursion_depth 来控制递归调用的层数上限。如下例设为12.  
  235.   
  236.   
  237. mysql> set max_sp_recursion_depth=12;  
  238. Query OK, 0 rows affected (0.00 sec)  
  239.   
  240.    
  241.   
  242. 优点 : 可以更灵活处理,及层数的显示。并且可以按照树的遍历顺序得到结果。  
  243.   
  244. 缺点 : 递归有255的限制。  
  245.   
  246.    
  247.   
  248.   
  249. 方法三:利用中间表和过程  
  250.   
  251. (本方法由yongyupost2000提供样子改编)  
  252.   
  253. 创建存储过程如下。由于MySQL中不允许在同一语句中对临时表多次引用,只以使用普通表tmpLst来实现了。当然你的程序中负责在用完后清除这个表。  
  254.   
  255.    
  256.   
  257.   
  258. delimiter //  
  259.   
  260. drop PROCEDURE IF EXISTS  showTreeNodes_yongyupost2000//  
  261.   
  262. CREATE PROCEDURE showTreeNodes_yongyupost2000 (IN rootid INT)  
  263. BEGIN  
  264.  DECLARE Level int ;  
  265.  drop TABLE IF EXISTS tmpLst;  
  266.  CREATE TABLE tmpLst (  
  267.   id int,  
  268.   nLevel int,  
  269.   sCort varchar(8000)  
  270.  );  
  271.    
  272.  Set Level=0 ;  
  273.  INSERT into tmpLst SELECT id,Level,ID FROM treeNodes WHERE PID=rootid;  
  274.  WHILE ROW_COUNT()>0 DO  
  275.   SET Level=Level+1 ;  
  276.   INSERT into tmpLst   
  277.    SELECT A.ID,Level,concat(B.sCort,A.ID) FROM treeNodes A,tmpLst B   
  278.     WHERE  A.PID=B.ID AND B.nLevel=Level-1  ;  
  279.  END WHILE;  
  280.     
  281. END;  
  282. //  
  283.   
  284. delimiter ;  
  285.   
  286. CALL showTreeNodes_yongyupost2000(0);  
  287.   
  288.   
  289.   
  290. 执行完后会产生一个tmpLst表,nLevel 为节点深度,sCort 为排序字段。  
  291. 使用方法  
  292.   
  293.   
  294.   
  295.   
  296. SELECT concat(SPACE(B.nLevel*2),'+--',A.nodename)  
  297. FROM treeNodes A,tmpLst B   
  298. WHERE A.ID=B.ID   
  299. ORDER BY B.sCort;  
  300.   
  301. +--------------------------------------------+  
  302. | concat(SPACE(B.nLevel*2),'+--',A.nodename) |  
  303. +--------------------------------------------+  
  304. | +--A                                       |  
  305. |   +--B                                     |  
  306. |     +--D                                   |  
  307. |     +--E                                   |  
  308. |   +--C                                     |  
  309. |     +--F                                   |  
  310. |       +--G                                 |  
  311. | +--H                                       |  
  312. |   +--J                                     |  
  313. |   +--K                                     |  
  314. |   +--I                                     |  
  315. |     +--L                                   |  
  316. |       +--N                                 |  
  317. |       +--O                                 |  
  318. |         +--P                               |  
  319. |         +--Q                               |  
  320. |     +--M                                   |  
  321. +--------------------------------------------+  
  322. 17 rows in set (0.00 sec)  
  323.   
  324.    
  325.   
  326.   
  327.    
  328.   
  329.   
  330.    
  331.   
  332. 优点 : 层数的显示。并且可以按照树的遍历顺序得到结果。没有递归限制。  
  333. 缺点 : MySQL中对临时表的限制,只能使用普通表,需做事后清理。  
  334.   
  335.    
  336.   
  337. 以上是几个在MySQL中用存储过程比较简单的实现方法。  
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
要获取MySQL数据库树状结构的所有子节点,您可以使用递归查询或者使用闭包表方法。以下是两种方法的示例: 1. 递归查询方法: 假设您的树状结构表名为 `tree_table`,其包含 `id`(节点ID)和 `parent_id`(父节点ID)两列。您可以使用以下查询来获取指定节点及其所有子节点: ```sql WITH RECURSIVE sub_nodes AS ( SELECT id, parent_id FROM tree_table WHERE id = <指定节点ID> UNION ALL SELECT tt.id, tt.parent_id FROM tree_table tt INNER JOIN sub_nodes sn ON tt.parent_id = sn.id ) SELECT id FROM sub_nodes; ``` 请将 `<指定节点ID>` 替换为您要获取子节点的节点ID。 2. 闭包表方法: 闭包表是一种存储每个节点之间关系的技术,它可以在一张表保存所有祖先和后代节点之间的关系。假设您的树状结构表名为 `tree_table`,并且您已经创建了一个名为 `closure_table` 的闭包表,其包含 `ancestor_id` 和 `descendant_id` 两列。 首先,您需要使用以下查询来生成闭包表: ```sql INSERT INTO closure_table (ancestor_id, descendant_id) SELECT t1.id, t2.id FROM tree_table t1 JOIN tree_table t2 ON t1.id <= t2.id START WITH t1.id = <根节点ID> CONNECT BY PRIOR t2.id = t2.parent_id; ``` 请将 `<根节点ID>` 替换为您的树状结构的根节点ID。 然后,您可以使用以下查询来获取指定节点及其所有子节点: ```sql SELECT descendant_id FROM closure_table WHERE ancestor_id = <指定节点ID>; ``` 请将 `<指定节点ID>` 替换为您要获取子节点的节点ID。 这些方法都能够帮助您获取MySQL数据库树状结构的所有子节点。根据您的具体情况,选择合适的方法进行查询。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值