zoukankan      html  css  js  c++  java
  • [MySQL] 实现树形的遍历(关于多级菜单栏以及多级上下部门的查询问题)

    前言:

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

    如果在执行SQL语句的时候报错,删除END后的¥符号即可



    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> 


    参考网址: 
    http://jan.kneschke.de/projects/mysql/sp/sp_tree.sql
    http://blog.csdn.net/ylqmf/article/details/5172901

  • 相关阅读:
    react 封装antd menu组件,路由懒加载,可折叠,可配置显示和隐藏,刷新后选择正确的菜单,打开正确的submenu
    useHistory做页面跳转导航
    react-draft-wysiwyg富文本组件
    html转json json转html
    create-react-app 生成 report.html 可视化打包分析
    axios设置请求头实现post请求发送数据的格式(Form Data)
    url查询参数中的汉字如何解码
    React在body下追加全局组件并实现渲染更新
    Loadrunner-08-增强和优化脚本-检查点
    Loadrunner-06-增强和优化脚本-事务
  • 原文地址:https://www.cnblogs.com/myhappylife/p/6395354.html
Copyright © 2011-2022 走看看