• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    MySQL 实现树的遍历详解及简单实现示例

    MySQL 实现树的遍历

    经常在一个表中有父子关系的两个字段,比如empno与manager,这种结构中需要用到树的遍历。在Oracle 中可以使用connect by简单解决问题,但MySQL 5.1中还不支持(据说已纳入to do中),要自己写过程或函数来实现。

    一、建立测试表和数据:

    
    DROP TABLE IF EXISTS `channel`; 
     
    CREATE TABLE `channel` ( 
     `id` int(11) NOT NULL AUTO_INCREMENT,  
     `cname` varchar(200) DEFAULT NULL, 
     `parent_id` int(11) DEFAULT NULL, 
     PRIMARY KEY (`id`) 
    ) ENGINE=MyISAM AUTO_INCREMENT=19 DEFAULT CHARSET=utf8; 
     
    /*Data for the table `channel` */ 
     
    insert into `channel`(`id`,`cname`,`parent_id`)  
    values (13,'首页',-1), 
        (14,'TV580',-1), 
        (15,'生活580',-1), 
        (16,'左上幻灯片',13), 
        (17,'帮忙',14), 
        (18,'栏目简介',17); 
    
    

     二、利用临时表和递归过程实现树的遍历(MySQL的UDF不能递归调用):

    
    DELIMITER $$ 
     
    USE `db1`$$ 
     
     
    -- 从某节点向下遍历子节点 
    -- 递归生成临时表数据 
    DROP PROCEDURE IF EXISTS `createChildLst`$$ 
     
    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 channel WHERE parent_id=rootId; 
       DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; 
       SET max_sp_recursion_depth=12; 
       
       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$$ 
     
     
    -- 从某节点向上追溯根节点 
    -- 递归生成临时表数据 
    DROP PROCEDURE IF EXISTS `createParentLst`$$ 
     
    CREATE PROCEDURE `createParentLst`(IN rootId INT,IN nDepth INT) 
    BEGIN 
       DECLARE done INT DEFAULT 0; 
       DECLARE b INT; 
       DECLARE cur1 CURSOR FOR SELECT parent_id FROM channel WHERE id=rootId; 
       DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; 
       SET max_sp_recursion_depth=12; 
       
       INSERT INTO tmpLst VALUES (NULL,rootId,nDepth); 
       
       OPEN cur1; 
       
       FETCH cur1 INTO b; 
       WHILE done=0 DO 
           CALL createParentLst(b,nDepth+1); 
           FETCH cur1 INTO b; 
       END WHILE; 
       
       CLOSE cur1; 
       END$$ 
     
     
    -- 实现类似Oracle SYS_CONNECT_BY_PATH的功能 
    -- 递归过程输出某节点id路径 
    DROP PROCEDURE IF EXISTS `createPathLst`$$ 
     
    CREATE PROCEDURE `createPathLst`(IN nid INT,IN delimit VARCHAR(10),INOUT pathstr VARCHAR(1000)) 
    BEGIN          
       DECLARE done INT DEFAULT 0; 
       DECLARE parentid INT DEFAULT 0;    
       DECLARE cur1 CURSOR FOR  
       SELECT t.parent_id,CONCAT(CAST(t.parent_id AS CHAR),delimit,pathstr) 
        FROM channel AS t WHERE t.id = nid; 
         
       DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; 
       SET max_sp_recursion_depth=12;          
       
       OPEN cur1; 
       
       FETCH cur1 INTO parentid,pathstr; 
       WHILE done=0 DO       
           CALL createPathLst(parentid,delimit,pathstr); 
           FETCH cur1 INTO parentid,pathstr; 
       END WHILE; 
          
       CLOSE cur1;  
       END$$ 
     
     
    -- 递归过程输出某节点name路径 
    DROP PROCEDURE IF EXISTS `createPathnameLst`$$ 
     
    CREATE PROCEDURE `createPathnameLst`(IN nid INT,IN delimit VARCHAR(10),INOUT pathstr VARCHAR(1000)) 
    BEGIN          
       DECLARE done INT DEFAULT 0; 
       DECLARE parentid INT DEFAULT 0;    
       DECLARE cur1 CURSOR FOR  
       SELECT t.parent_id,CONCAT(t.cname,delimit,pathstr) 
        FROM channel AS t WHERE t.id = nid; 
         
       DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; 
       SET max_sp_recursion_depth=12;          
       
       OPEN cur1; 
       
       FETCH cur1 INTO parentid,pathstr; 
       WHILE done=0 DO       
           CALL createPathnameLst(parentid,delimit,pathstr); 
           FETCH cur1 INTO parentid,pathstr; 
       END WHILE; 
          
       CLOSE cur1;  
       END$$ 
     
     
    -- 调用函数输出id路径 
    DROP FUNCTION IF EXISTS `fn_tree_path`$$ 
     
    CREATE FUNCTION `fn_tree_path`(nid INT,delimit VARCHAR(10)) RETURNS VARCHAR(2000) CHARSET utf8 
    BEGIN  
     DECLARE pathid VARCHAR(1000); 
      
     SET @pathid=CAST(nid AS CHAR); 
     CALL createPathLst(nid,delimit,@pathid); 
      
     RETURN @pathid; 
    END$$ 
     
     
    -- 调用函数输出name路径 
    DROP FUNCTION IF EXISTS `fn_tree_pathname`$$ 
     
    CREATE FUNCTION `fn_tree_pathname`(nid INT,delimit VARCHAR(10)) RETURNS VARCHAR(2000) CHARSET utf8 
    BEGIN  
     DECLARE pathid VARCHAR(1000); 
      
     SET @pathid='';   
     CALL createPathnameLst(nid,delimit,@pathid); 
      
     RETURN @pathid; 
    END$$ 
     
     
    -- 调用过程输出子节点 
    DROP PROCEDURE IF EXISTS `showChildLst`$$ 
     
    CREATE PROCEDURE `showChildLst`(IN rootId INT) 
    BEGIN 
       DROP TEMPORARY TABLE IF EXISTS tmpLst; 
       CREATE TEMPORARY TABLE IF NOT EXISTS tmpLst  
        (sno INT PRIMARY KEY AUTO_INCREMENT,id INT,depth INT);    
       
       CALL createChildLst(rootId,0); 
       
       SELECT channel.id,CONCAT(SPACE(tmpLst.depth*2),'--',channel.cname) NAME,channel.parent_id,tmpLst.depth,fn_tree_path(channel.id,'/') path,fn_tree_pathname(channel.id,'/') pathname 
       FROM tmpLst,channel WHERE tmpLst.id=channel.id ORDER BY tmpLst.sno; 
       END$$ 
     
    -- 调用过程输出父节点 
    DROP PROCEDURE IF EXISTS `showParentLst`$$ 
     
    CREATE PROCEDURE `showParentLst`(IN rootId INT) 
    BEGIN 
       DROP TEMPORARY TABLE IF EXISTS tmpLst; 
       CREATE TEMPORARY TABLE IF NOT EXISTS tmpLst  
        (sno INT PRIMARY KEY AUTO_INCREMENT,id INT,depth INT);    
       
       CALL createParentLst(rootId,0); 
       
       SELECT channel.id,CONCAT(SPACE(tmpLst.depth*2),'--',channel.cname) NAME,channel.parent_id,tmpLst.depth,fn_tree_path(channel.id,'/') path,fn_tree_pathname(channel.id,'/') pathname 
       FROM tmpLst,channel WHERE tmpLst.id=channel.id ORDER BY tmpLst.sno; 
       END$$ 
     
     
    DELIMITER ; 
    
    

    三、测试

    
    CALL showChildLst(-1); 
    CALL showChildLst(13); 
    CALL showChildLst(14); 
    CALL showChildLst(17); 
    CALL showChildLst(18); 
     
    CALL showParentLst(-1); 
    CALL showParentLst(13); 
    CALL showParentLst(14); 
    CALL showParentLst(17); 
    CALL showParentLst(18); 
    
    

    四、遗留问题

    1. 因为mysql对动态游标的支持不够,所以要想做成通用的过程或函数比较困难,可以利用两个临时表来转换(同时去掉了递归调用)是个相对通用的实现。

    2. 目前来看无论哪种实现,效率都不太好,希望mysql自己能实现Oracle 的connect by 功能,应该会比较优化。

    感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

    您可能感兴趣的文章:
    • 在Mysql数据库里通过存储过程实现树形的遍历
    • mysql存储过程详解
    • mysql存储过程 游标 循环使用介绍
    • MySQL存储过程使用实例详解
    • MySQL存储过程例子(包含事务,输出参数,嵌套调用)
    • MySql存储过程与函数详解
    • mysql存储过程基础之遍历多表记录后插入第三方表中详解
    上一篇:mysql默认编码为UTF-8 通过修改my.ini实现方法
    下一篇:Winserver2012下mysql 5.7解压版(zip)配置安装教程详解
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    MySQL 实现树的遍历详解及简单实现示例 MySQL,实现,树,的,遍历,详解,