mysql查询所有子节点,后代

总结查询mysql下 的所有子节点。。。希望一起学习。。

在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, 得到一个由所有子节点号组成的字符串.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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来查找

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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为调用入口过程,准备临时表及初始化。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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 ;

调用时传入结点

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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.

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

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

缺点 : 递归有255的限制。

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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 为排序字段。

使用方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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中对临时表的限制,只能使用普通表,需做事后清理。

查询

1、根据子节点查询所有的父节点  创建getParentList函数

  1. delimiter //  
  2. CREATEFUNCTION `getParentList`(rootId INT)  
  3. RETURNS varchar(1000)  
  4.   
  5. BEGIN  
  6. DECLARE sTempVARCHAR(1000);  
  7. DECLARE sTempParVARCHAR(1000);  
  8. SET sTemp ='';  
  9. SET sTempPar =rootId;  
  10.   
  11. #循环递归  
  12. WHILE sTempPar is not null DO  
  13. #判断是否是第一个,不加的话第一个会为空  
  14. IF sTemp !='' THEN  
  15. SET sTemp =concat(sTemp,',',sTempPar);  
  16. ELSE  
  17. SET sTemp = sTempPar;  
  18. ENDIF;  
  19.   
  20. SET sTemp =concat(sTemp,',',sTempPar);  
  21. SELECTgroup_concat(pid) INTO sTempPar FROM province where pid<>idand FIND_IN_SET(id,sTempPar)>0;  
  22. ENDWHILE;  
  23.   
  24. RETURN sTemp;  
  25. END  
  26. //  

此时Mysql可能会报如下错误:


解决方法:

执行此语句:show VARIABLES like "log_bin_trust_function_creators";

发现log_bin_trust_function_creators的值为OFF(这是默认值)

那么,我们再执行语句:set global log_bin_trust_function_creators = 1;

现在再来查看log_bin_trust_function_creators的值已经变为ON了


最后,函数就可以创建成功了。

新建一张数据表province

[sql]  view plain  copy
  1. SET FOREIGN_KEY_CHECKS=0;  
  2. -- ----------------------------  
  3. -- Table structure for province  
  4. -- ----------------------------  
  5. DROP TABLE IF EXISTS `province`;  
  6. CREATE TABLE `province` (  
  7.   `id` int(10) NOT NULL,  
  8.   `namevarchar(10) NOT NULL,  
  9.   `pid` int(10) unsigned NOT NULL,  
  10.   PRIMARY KEY (`id`)  
  11. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  
  12. -- ----------------------------  
  13. -- Records of province  
  14. -- ----------------------------  
  15. INSERT INTO `province` VALUES ('1''福建''0');  
  16. INSERT INTO `province` VALUES ('2''湖南''0');  
  17. INSERT INTO `province` VALUES ('3''湖北''0');  
  18. INSERT INTO `province` VALUES ('4''长沙''2');  
  19. INSERT INTO `province` VALUES ('5''郴州''2');  
  20. INSERT INTO `province` VALUES ('6''武汉''3');  
  21. INSERT INTO `province` VALUES ('7''武昌''3');  
  22. INSERT INTO `province` VALUES ('8''厦门''1');  
  23. INSERT INTO `province` VALUES ('9''福州''1');  
  24. INSERT INTO `province` VALUES ('10''泉州''1');  
  25. INSERT INTO `province` VALUES ('11''闽侯''9');  
  26. INSERT INTO `province` VALUES ('12''长乐''9');  
  27. INSERT INTO `province` VALUES ('13''安溪''10');  
  28. INSERT INTO `province` VALUES ('14''晋江''10');  
  29. INSERT INTO `province` VALUES ('15''凤城''13');  
  30. INSERT INTO `province` VALUES ('16''参内''13');  
  31. INSERT INTO `province` VALUES ('17''龙湖''15');  


执行查询语句:select * from province where FIND_IN_SET(id,getParentList(17))

查询结果:


2、根据父节点查找所有的子节点  创建函数getChildrenList

[sql]  view plain  copy
  1. delimiter //  
  2. CREATE FUNCTION `getChildrenList`(rootId INT)  
  3. RETURNS varchar(1000) 

  4. BEGIN  
  5. DECLARE sTemp VARCHAR(1000);  
  6. DECLARE sTempChd VARCHAR(1000);  

  7. SET sTemp = '$';  
  8. SET sTempChd =cast(rootId as CHAR);  
  9.   
  10.   
  11. WHILE sTempChd is not null DO  
  12. SET sTemp = concat(sTemp,',',sTempChd);  
  13. SELECT group_concat(id) INTO sTempChd FROM province where FIND_IN_SET(pid,sTempChd)>0;  
  14. END WHILE;  
  15. RETURN sTemp;  
  16. END  
  17. //  

执行查询语句:select * from province where FIND_IN_SET(id,getChildrenList(1))


例子

执行

CREATE TABLE `treenodes` ( 

  `id` int(11) NOT NULL, 

  `nodename` varchar(20) DEFAULT NULL, 

  `pid` int(11) DEFAULT NULL, 

  PRIMARY KEY (`id`) 

) ENGINE=InnoDB DEFAULT CHARSET=latin1;

INSERT INTO `treenodes` VALUES ('1', 'A', '0'); 

INSERT INTO `treenodes` VALUES ('2', 'B', '1'); 

INSERT INTO `treenodes` VALUES ('3', 'C', '1'); 

INSERT INTO `treenodes` VALUES ('4', 'D', '2'); 

INSERT INTO `treenodes` VALUES ('5', 'E', '2'); 

INSERT INTO `treenodes` VALUES ('6', 'F', '3'); 

INSERT INTO `treenodes` VALUES ('7', 'G', '6'); 

INSERT INTO `treenodes` VALUES ('8', 'H', '0'); 

INSERT INTO `treenodes` VALUES ('9', 'I', '8'); 

INSERT INTO `treenodes` VALUES ('10', 'J', '8'); 

INSERT INTO `treenodes` VALUES ('11', 'K', '8'); 

INSERT INTO `treenodes` VALUES ('12', 'L', '9'); 

INSERT INTO `treenodes` VALUES ('13', 'M', '9'); 

INSERT INTO `treenodes` VALUES ('14', 'N', '12'); 

INSERT INTO `treenodes` VALUES ('15', 'O', '12'); 

INSERT INTO `treenodes` VALUES ('16', 'P', '15'); 

INSERT INTO `treenodes` VALUES ('17', 'Q', '15');

表如下mysql查询所有子节点,后代_第1张图片

查询父节点的函数

delimiter // 
CREATE FUNCTION `getParentList`(rootId INT) 
     RETURNS char(255) 
     BEGIN 
      declare fid int default 1;
      declare str char(255default rootId;
      while rootId>0 do
      set fid=(SELECT pid FROM hostmonitor.treenodes  WHERE rootId=id); 
     IF fid > 0 THEN  
     SET str=concat(str,',',fid);   
     SET rootId=fid;  
     ELSE SET rootId=fid;  
     END IF;  
     END WHILE;
 return str;
     END  //
调用select getParentList(7);

 查询子节点

delimiter //
 CREATE FUNCTION `getChildList`(rootId INT) 
     RETURNS char(255) 
     BEGIN 
       DECLARE str char(255) ; 
       DECLARE cid char(255) ; 
     
       SET str = ''; 
       SET cid =cast(rootId as CHAR); 
     
       WHILE cid is not null DO 
         SET strconcat(str,',',cid); 
         SELECT group_concat(idINTO cid FROM treeNodes where FIND_IN_SET(pid,cid)>0; 
       END WHILE; 
       RETURN str; 
     END //    
select getChildLst(1);

在看一个例子

一、查父集合


Sql代码:

[sql]  view plain  copy
  1. --drop FUNCTION `getParentList`  
  2. CREATE FUNCTION `getParentList`(rootId varchar(100))   
  3. RETURNS varchar(1000)   
  4. BEGIN   
  5. DECLARE fid varchar(100) default '';   
  6. DECLARE str varchar(1000) default rootId;   
  7.   
  8. WHILE rootId is not null  do   
  9.     SET fid =(SELECT parentid FROM treeNodes WHERE id = rootId);   
  10.     IF fid is not null THEN   
  11.         SET str = concat(str, ',', fid);   
  12.         SET rootId = fid;   
  13.     ELSE   
  14.         SET rootId = fid;   
  15.     END IF;   
  16. END WHILE;   
  17. return str;  
  18. END  

查询:

Sql代码

[sql]  view plain  copy
  1. select getParentList('001001001001001');   
  2.   
  3. select * from sbkfwh where FIND_IN_SET(id,getParentList('001001001001002'))   

二、查子集合 

Java代码:

[sql]  view plain  copy
  1. --drop FUNCTION `getChildList`  
  2. CREATE FUNCTION `getChildList`(rootId varchar(100))   
  3. RETURNS varchar(2000)  
  4. BEGIN   
  5. DECLARE str varchar(2000);  
  6. DECLARE cid varchar(100);   
  7. SET str = '$';   
  8. SET cid = rootId;   
  9. WHILE cid is not null DO   
  10.     SET str = concat(str, ',', cid);   
  11.     SELECT group_concat(id) INTO cid FROM treeNodes where FIND_IN_SET(parentid, cid) > 0;   
  12. END WHILE;   
  13. RETURN str;   
  14. END  

查询

Sql代码

[sql]  view plain  copy
  1. select getParentList('001001001');   
  2. select * from sbkfwh where FIND_IN_SET(id,getChildList('001001001'))  

Mysql函数中并不支持动态sql,Dynamic SQL is not allowed in stored function or trigger

要想查多个表的,可以建多个函数,或用以下方法

[sql]  view plain  copy
  1. drop FUNCTION `getChildListTest`     
  2. CREATE FUNCTION `getChildListTest`(tableName varchar(64),rootId varchar(100))      
  3. RETURNS varchar(2000)     
  4. BEGIN      
  5. DECLARE str varchar(2000);     
  6. DECLARE cid varchar(100);      
  7. SET str = '$';      
  8. SET cid = rootId;      
  9.   
  10. IF tableName = 'tableName1' THEN    
  11.         WHILE cid is not null DO      
  12.                 SET str = concat(str, ',', cid);      
  13.                 SELECT group_concat(id) INTO cid FROM tableName1 where FIND_IN_SET(parentid, cid) > 0;     
  14.         END WHILE;    
  15. ELSEIF tableName = 'tableName2' THEN    
  16.         WHILE cid is not null DO      
  17.                 SET str = concat(str, ',', cid);      
  18.                 SELECT group_concat(id) INTO cid FROM tableName2 where FIND_IN_SET(parentid, cid) > 0;     
  19.         END WHILE;    
  20. END IF;    
  21.   
  22. RETURN str;      
  23. END  
  24. 自己动手,多多心动。


对比mysql递归树两种查询方式效率。

mysql查询所有子节点,后代_第2张图片
mysql查询所有子节点,后代_第3张图片
  1. 1

    -


    DROP TABLE IF EXISTS `t_areainfo`;
    CREATE TABLE `t_areainfo` (
     `id` int(11) NOT '0' AUTO_INCREMENT,
     `level` int(11) DEFAULT '0',
     `name` varchar(255) DEFAULT '0',
     `parentId` int(11) DEFAULT '0',
     `status` int(11) DEFAULT '0',
     PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=65 DEFAULT CHARSET=utf8;

  2. 2

    --初始数据


    INSERT INTO `t_areainfo` VALUES ('1', '0', '中国', '0', '0');
    INSERT INTO `t_areainfo` VALUES ('2', '0', '华北区', '1', '0');
    INSERT INTO `t_areainfo` VALUES ('3', '0', '华南区', '1', '0');
    INSERT INTO `t_areainfo` VALUES ('4', '0', '北京', '2', '0');
    INSERT INTO `t_areainfo` VALUES ('5', '0', '海淀区', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('6', '0', '丰台区', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('7', '0', '朝阳区', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('8', '0', '北京XX区1', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('9', '0', '北京XX区2', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('10', '0', '北京XX区3', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('11', '0', '北京XX区4', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('12', '0', '北京XX区5', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('13', '0', '北京XX区6', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('14', '0', '北京XX区7', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('15', '0', '北京XX区8', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('16', '0', '北京XX区9', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('17', '0', '北京XX区10', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('18', '0', '北京XX区11', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('19', '0', '北京XX区12', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('20', '0', '北京XX区13', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('21', '0', '北京XX区14', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('22', '0', '北京XX区15', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('23', '0', '北京XX区16', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('24', '0', '北京XX区17', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('25', '0', '北京XX区18', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('26', '0', '北京XX区19', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('27', '0', '北京XX区1', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('28', '0', '北京XX区2', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('29', '0', '北京XX区3', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('30', '0', '北京XX区4', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('31', '0', '北京XX区5', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('32', '0', '北京XX区6', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('33', '0', '北京XX区7', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('34', '0', '北京XX区8', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('35', '0', '北京XX区9', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('36', '0', '北京XX区10', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('37', '0', '北京XX区11', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('38', '0', '北京XX区12', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('39', '0', '北京XX区13', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('40', '0', '北京XX区14', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('41', '0', '北京XX区15', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('42', '0', '北京XX区16', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('43', '0', '北京XX区17', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('44', '0', '北京XX区18', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('45', '0', '北京XX区19', '4', '0');
    INSERT INTO `t_areainfo` VALUES ('46', '0', 'xx省1', '1', '0');
    INSERT INTO `t_areainfo` VALUES ('47', '0', 'xx省2', '1', '0');
    INSERT INTO `t_areainfo` VALUES ('48', '0', 'xx省3', '1', '0');
    INSERT INTO `t_areainfo` VALUES ('49', '0', 'xx省4', '1', '0');
    INSERT INTO `t_areainfo` VALUES ('50', '0', 'xx省5', '1', '0');
    INSERT INTO `t_areainfo` VALUES ('51', '0', 'xx省6', '1', '0');
    INSERT INTO `t_areainfo` VALUES ('52', '0', 'xx省7', '1', '0');
    INSERT INTO `t_areainfo` VALUES ('53', '0', 'xx省8', '1', '0');
    INSERT INTO `t_areainfo` VALUES ('54', '0', 'xx省9', '1', '0');
    INSERT INTO `t_areainfo` VALUES ('55', '0', 'xx省10', '1', '0');
    INSERT INTO `t_areainfo` VALUES ('56', '0', 'xx省11', '1', '0');
    INSERT INTO `t_areainfo` VALUES ('57', '0', 'xx省12', '1', '0');
    INSERT INTO `t_areainfo` VALUES ('58', '0', 'xx省13', '1', '0');
    INSERT INTO `t_areainfo` VALUES ('59', '0', 'xx省14', '1', '0');
    INSERT INTO `t_areainfo` VALUES ('60', '0', 'xx省15', '1', '0');
    INSERT INTO `t_areainfo` VALUES ('61', '0', 'xx省16', '1', '0');
    INSERT INTO `t_areainfo` VALUES ('62', '0', 'xx省17', '1', '0');
    INSERT INTO `t_areainfo` VALUES ('63', '0', 'xx省18', '1', '0');
    INSERT INTO `t_areainfo` VALUES ('64', '0', 'xx省19', '1', '0');


:采用function获取所有子节点的id

  1. 1

    --查询传入areaId及其以下所有子节点

    DROP FUNCTION IF EXISTS queryChildrenAreaInfo;

    CREATE FUNCTION `queryChildrenAreaInfo` (areaId INT)

    RETURNS VARCHAR(4000)

    BEGIN

    DECLARE sTemp VARCHAR(4000);

    DECLARE sTempChd VARCHAR(4000);


    SET sTemp = '$';

    SET sTempChd = cast(areaId as char);


    WHILE sTempChd is not NULL DO

    SET sTemp = CONCAT(sTemp,',',sTempChd);

    SELECT group_concat(id) INTO sTempChd FROM t_areainfo where FIND_IN_SET(parentId,sTempChd)>0;

    END WHILE;

    return sTemp;

    END;

  2. 2

    --调用方式

    select queryChildrenAreaInfo(1);

    select * from t_areainfo where FIND_IN_SET(id, queryChildrenAreaInfo(1)); 

    END

:采用临时表和存储过程完成

  1. 1

    -- 创建存储过程

    drop PROCEDURE showChildList;

    CREATE PROCEDURE showChildList (IN rootId INT)

    BEGIN

    CREATE TEMPORARY TABLE

    IF NOT EXISTS tmpList (

    sno INT PRIMARY KEY auto_increment,

    id INT,

    depth INT

    );


    DELETE FROM tmpList;


    CALL createChildList (rootId, 0);


    SELECT tmpList.*, t_areainfo.* FROM tmpList, t_areainfo

    WHERE

    tmpList.id = t_areainfo.id

    ORDER BY

    tmpList.sno;

    END;


    drop PROCEDURE createChildList;

    CREATE PROCEDURE createChildList (IN rootId INT, IN nDepth INT)

    BEGIN

    DECLARE done INT DEFAULT 0;

    DECLARE b INT;


    DECLARE cur1 CURSOR FOR SELECT id FROM t_areainfo WHERE parentId = rootId;

    DECLARE CONTINUE HANDLER FOR NOT FOUND

    SET done = 1;


    INSERT INTO tmpList VALUES (NULL, rootId, nDepth);


    OPEN cur1;

    FETCH cur1 INTO b;


    WHILE done = 0 DO

    CALL createChildList (b, nDepth + 1);

    FETCH cur1 INTO b;


    END WHILE;

    CLOSE cur1;

    END;


  2. 2

    -- 调用方式

    call showChildList(1);


  1. 1

    --简易程度

    首先我们可以通过sql语句就可以看的出,方式二的代码量差不多是方式一的两倍,而且又是临时表又是游标的,极易出错。

    --效率对比

    可以通过图片可以看到,同样的查询结果,方式一仅仅需要0.044s既可以完成查询,而方式二则需要1.525s,效率远远低于方式一。

    END

结论:

  1. 1

    强烈推荐用方式一

    END

注意事项

  • 执行方式二是系统出报错,错误原因是因为没有指定控制递归调用层数上线,可以通过利用系统参数 max_sp_recursion_depth 来控制递归调用的层数上限。


你可能感兴趣的:(mysql查询所有子节点,后代)