• 技术文章 >数据库 >mysql教程

    实现树形的遍历(关于多级菜单栏以及多级上下部门的查询问题)_MySQL

    2016-05-30 17:11:17原创461
    前言:

    关于多级别菜单栏或者权限系统中部门上下级的树形遍历,oracle中有connect by来实现,mysql没有这样的便捷途径,所以MySQL遍历数据表是我们经常会遇到的头痛问题,下面通过存储过程来实现。

    1,建立测试表和数据:

    DROP TABLE IF EXISTS csdn.channel;   
    CREATE TABLE csdn.channel (   
      id INT(11) NOT NULL AUTO_INCREMENT,     
      cname VARCHAR(200) DEFAULT NULL,   
      parent_id INT(11) DEFAULT NULL,   
      PRIMARY KEY (id)   
    ) ENGINE=INNODB DEFAULT CHARSET=utf8;   
    INSERT  INTO channel(id,cname,parent_id)    
    VALUES (13,‘首页‘,-1),   
           (14,‘TV580‘,-1),   
           (15,‘生活580‘,-1),   
           (16,‘左上幻灯片‘,13),   
           (17,‘帮忙‘,14),   
           (18,‘栏目简介‘,17);  
    DROP TABLE IF EXISTS channel;

    2,利用临时表和递归过程实现树的遍历(mysql的UDF不能递归调用):

    2.1,从某节点向下遍历子节点,递归生成临时表数据

    -- pro_cre_childlist
    DELIMITER $$     
    DROP PROCEDURE IF EXISTS csdn.pro_cre_childlist$$   
    CREATE PROCEDURE csdn.pro_cre_childlist(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 pro_cre_childlist(b,nDepth+1);   
                  FETCH cur1 INTO b;   
          END WHILE;   
           
          CLOSE cur1;   
    END$$   

    2.2,从某节点向上追溯根节点,递归生成临时表数据

    -- pro_cre_parentlist
    DELIMITER $$
    DROP PROCEDURE IF EXISTS csdn.pro_cre_parentlist$$   
    CREATE PROCEDURE csdn.pro_cre_parentlist(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 pro_cre_parentlist(b,nDepth+1);   
                  FETCH cur1 INTO b;   
          END WHILE;   
           
          CLOSE cur1;   
         END$$   

    2.3,实现类似Oracle SYS_CONNECT_BY_PATH的功能,递归过程输出某节点id路径

    -- pro_cre_pathlist
    DELIMITER $$
    USE csdn$$
    DROP PROCEDURE IF EXISTS pro_cre_pathlist$$
    CREATE PROCEDURE pro_cre_pathlist(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 pro_cre_pathlist(parentid,delimit,pathstr);   
                  FETCH cur1 INTO parentid,pathstr;   
          END WHILE;   
                
          CLOSE cur1;    
    END$$
    
    
    DELIMITER ;
    

    2.4,递归过程输出某节点name路径

    -- pro_cre_pnlist
    DELIMITER $$
    USE csdn$$
    DROP PROCEDURE IF EXISTS pro_cre_pnlist$$
    CREATE PROCEDURE pro_cre_pnlist(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 pro_cre_pnlist(parentid,delimit,pathstr);   
                  FETCH cur1 INTO parentid,pathstr;   
          END WHILE;   
                
          CLOSE cur1;    
         END$$
    
    
    DELIMITER ;
    

    2.5,调用函数输出id路径

    -- fn_tree_path
    DELIMITER $$ 
    DROP FUNCTION IF EXISTS csdn.fn_tree_path$$   
    CREATE FUNCTION csdn.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 pro_cre_pathlist(nid,delimit,@pathid);   
         
      RETURN @pathid;   
    END$$   
      
      

    2.6,调用函数输出name路径

    -- fn_tree_pathname
    -- 调用函数输出name路径   
    DELIMITER $$ 
    DROP FUNCTION IF EXISTS csdn.fn_tree_pathname$$   
    CREATE FUNCTION csdn.fn_tree_pathname(nid INT,delimit VARCHAR(10)) RETURNS VARCHAR(2000) CHARSET utf8   
    BEGIN     
      DECLARE pathid VARCHAR(1000);   
      SET @pathid=‘‘;       
      CALL pro_cre_pnlist(nid,delimit,@pathid);   
      RETURN @pathid;   
    END$$  
    DELIMITER ; 
      

    2.7,调用过程输出子节点

    -- pro_show_childLst  
    DELIMITER $$
    -- 调用过程输出子节点   
    DROP PROCEDURE IF EXISTS pro_show_childLst$$   
    CREATE PROCEDURE pro_show_childLst(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 pro_cre_childlist(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$$   

    2.8,调用过程输出父节点

    -- pro_show_parentLst
    DELIMITER $$
    -- 调用过程输出父节点   
    DROP PROCEDURE IF EXISTS `pro_show_parentLst`$$   
    CREATE PROCEDURE `pro_show_parentLst`(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 pro_cre_parentlist(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$$   
    

    3,开始测试:

    3.1,从根节点开始显示,显示子节点集合:

    mysql> CALL pro_show_childLst(-1); 
    +----+-----------------------+-----------+-------+-------------+----------------------------+
    | id | NAME                  | parent_id | depth | path        | pathname                   |
    +----+-----------------------+-----------+-------+-------------+----------------------------+
    | 13 |   --首页              |        -1 |     1 | -1/13       | 首页/                      |
    | 16 |     --左上幻灯片      |        13 |     2 | -1/13/16    | 首页/左上幻灯片/           |
    | 14 |   --TV580             |        -1 |     1 | -1/14       | TV580/                     |
    | 17 |     --帮忙            |        14 |     2 | -1/14/17    | TV580/帮忙/                |
    | 18 |       --栏目简介      |        17 |     3 | -1/14/17/18 | TV580/帮忙/栏目简介/       |
    | 15 |   --生活580           |        -1 |     1 | -1/15       | 生活580/                   |
    +----+-----------------------+-----------+-------+-------------+----------------------------+
    6 rows in set (0.05 sec)
    
    
    Query OK, 0 rows affected (0.05 sec)
    

    3.2,显示首页下面的子节点

    CALL pro_show_childLst(13);  
    mysql> CALL pro_show_childLst(13);   
    +----+---------------------+-----------+-------+----------+-------------------------+
    | id | NAME                | parent_id | depth | path     | pathname                |
    +----+---------------------+-----------+-------+----------+-------------------------+
    | 13 | --首页              |        -1 |     0 | -1/13    | 首页/                   |
    | 16 |   --左上幻灯片      |        13 |     1 | -1/13/16 | 首页/左上幻灯片/        |
    +----+---------------------+-----------+-------+----------+-------------------------+
    2 rows in set (0.02 sec)
    
    
    Query OK, 0 rows affected (0.02 sec)
    
    
    mysql> 
    

    3.3,显示TV580下面的所有子节点

    CALL pro_show_childLst(14);   
    mysql> CALL pro_show_childLst(14);  
    +----+--------------------+-----------+-------+-------------+----------------------------+
    | id | NAME               | parent_id | depth | path        | pathname                   |
    +----+--------------------+-----------+-------+-------------+----------------------------+
    | 14 | --TV580            |        -1 |     0 | -1/14       | TV580/                     |
    | 17 |   --帮忙           |        14 |     1 | -1/14/17    | TV580/帮忙/                |
    | 18 |     --栏目简介     |        17 |     2 | -1/14/17/18 | TV580/帮忙/栏目简介/       |
    +----+--------------------+-----------+-------+-------------+----------------------------+
    3 rows in set (0.02 sec)
    
    
    Query OK, 0 rows affected (0.02 sec)
    
    
    mysql> 
    

    3.4,“帮忙”节点有一个子节点,显示出来:

    CALL pro_show_childLst(17);   
    mysql> CALL pro_show_childLst(17); 
    +----+------------------+-----------+-------+-------------+----------------------------+
    | id | NAME             | parent_id | depth | path        | pathname                   |
    +----+------------------+-----------+-------+-------------+----------------------------+
    | 17 | --帮忙           |        14 |     0 | -1/14/17    | TV580/帮忙/                |
    | 18 |   --栏目简介     |        17 |     1 | -1/14/17/18 | TV580/帮忙/栏目简介/       |
    +----+------------------+-----------+-------+-------------+----------------------------+
    2 rows in set (0.03 sec)
    
    
    Query OK, 0 rows affected (0.03 sec)
    
    
    mysql> 
    

    3.5,“栏目简介”没有子节点,所以只显示最终节点:

    mysql> CALL pro_show_childLst(18);   
    +----+----------------+-----------+-------+-------------+----------------------------+
    | id | NAME           | parent_id | depth | path        | pathname                   |
    +----+----------------+-----------+-------+-------------+----------------------------+
    | 18 | --栏目简介     |        17 |     0 | -1/14/17/18 | TV580/帮忙/栏目简介/       |
    +----+----------------+-----------+-------+-------------+----------------------------+
    1 row in set (0.36 sec)
    
    
    Query OK, 0 rows affected (0.36 sec)
    
    
    mysql> 

    3.6,显示根节点的父节点

    CALL pro_show_parentLst(-1);   
    mysql> CALL pro_show_parentLst(-1);
    Empty set (0.01 sec)
    
    
    Query OK, 0 rows affected (0.01 sec)
    
    
    mysql>
    

    3.7,显示“首页”的父节点

    CALL pro_show_parentLst(13);   
    mysql> CALL pro_show_parentLst(13);   
    +----+----------+-----------+-------+-------+----------+
    | id | NAME     | parent_id | depth | path  | pathname |
    +----+----------+-----------+-------+-------+----------+
    | 13 | --首页   |        -1 |     0 | -1/13 | 首页/    |
    +----+----------+-----------+-------+-------+----------+
    1 row in set (0.02 sec)
    
    
    Query OK, 0 rows affected (0.02 sec)
    
    
    mysql> 
    

    3.8,显示“TV580”的父节点,parent_id为-1

    CALL pro_show_parentLst(14);   
    mysql> CALL pro_show_parentLst(14);   
    +----+---------+-----------+-------+-------+----------+
    | id | NAME    | parent_id | depth | path  | pathname |
    +----+---------+-----------+-------+-------+----------+
    | 14 | --TV580 |        -1 |     0 | -1/14 | TV580/   |
    +----+---------+-----------+-------+-------+----------+
    1 row in set (0.02 sec)
    
    
    Query OK, 0 rows affected (0.02 sec)

    3.9,显示“帮忙”节点的父节点

    mysql>
    CALL pro_show_parentLst(17);   
    mysql> CALL pro_show_parentLst(17);   
    +----+-----------+-----------+-------+----------+---------------+
    | id | NAME      | parent_id | depth | path     | pathname      |
    +----+-----------+-----------+-------+----------+---------------+
    | 17 | --帮忙    |        14 |     0 | -1/14/17 | TV580/帮忙/   |
    | 14 |   --TV580 |        -1 |     1 | -1/14    | TV580/        |
    +----+-----------+-----------+-------+----------+---------------+
    2 rows in set (0.02 sec)
    
    
    Query OK, 0 rows affected (0.02 sec)
    
    
    mysql>
    

    3.10,显示最低层节点“栏目简介”的父节点

    CALL pro_show_parentLst(18);  
    mysql> CALL pro_show_parentLst(18);  
    +----+----------------+-----------+-------+-------------+----------------------------+
    | id | NAME           | parent_id | depth | path        | pathname                   |
    +----+----------------+-----------+-------+-------------+----------------------------+
    | 18 | --栏目简介     |        17 |     0 | -1/14/17/18 | TV580/帮忙/栏目简介/       |
    | 17 |   --帮忙       |        14 |     1 | -1/14/17    | TV580/帮忙/                |
    | 14 |     --TV580    |        -1 |     2 | -1/14       | TV580/                     |
    +----+----------------+-----------+-------+-------------+----------------------------+
    3 rows in set (0.02 sec)
    
    
    Query OK, 0 rows affected (0.02 sec)
    mysql>
    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    专题推荐:树形 菜单 上下
    上一篇:MySQL开发规范与使用技巧总结_MySQL 下一篇:jdbc连接mysql_MySQL
    Web大前端开发直播班

    相关文章推荐

    • 带你把MySQL索引吃透了• jquery mobile是什么意思• mysql怎么增加行• mysql怎么删除unique约束• mysql怎么增加权限

    全部评论我要评论

  • 取消发布评论发送
  • 1/1

    PHP中文网