最近在做表数据整理的时候碰到这样的一个问题,我有一张permission表,其数据结构为树形结构,里面有个permission_path字段用于记录根节点到父节点的路径(以permission_id为路径)。

  例子:假设100的父节点为10,10的父节点为1,这100的路径为:1/10。

 

  但现在有个问题,permission_path字段在当时并没有处理(为空字符串),如今这个表有四五百条数据,写代码来改太麻烦,更别说一条条数据手动修改,不可行!

  能否通过一个语句块来自动生成路径?显然是可以的,因为现在的oracle、SQL SERVER2005以上版本和mysql5.0以上版本的数据库都支持存储过程,而存储过程还有个比较有趣的玩法,那就是递归调用!用这个方法就可以快速、方便地解决上述问题。

  存储过程的概念这里就不介绍了,语句非常简单,直接上语句(我使用的数据库是mysql5.6),一步步解析:



CREATE PROCEDURE test(IN parent int)
BEGIN 
DECLARE ids VARCHAR(255) DEFAULT '0';   
DECLARE IND INT;
DECLARE i INT DEFAULT 1;
DECLARE path VARCHAR(255);
SELECT GROUP_CONCAT(t.permission_id) INTO ids from system_permissions t where t.permission_parent = parent;
SELECT COUNT(t.*) INTO IND from system_permissions t where t.permission_parent = parent;
SELECT t.permission_path INTO path from system_permissions t where t.permission_id = parent;
if ids <> '0' THEN
UPDATE system_permissions set permission_path = CONCAT(path,'/',parent) where permission_parent = parent;
SET @@max_sp_recursion_depth = 100;  
while i <= IND  DO
call test(substring_index(substring_index(ids,',', i), ',', -1));
set i = i + 1;
end while;
end if;
end



  先介绍基本语法:



create procedure test(IN p1 int,IN p2 varchar2(10))      //create procedure 函数名(IN 参数1 类型,IN 参数2 类型),英文部分为固定写法,
                                    中文部分为自定义,其中类型只能使用数据库存在的类型

  begin   //标识着存储过程逻辑的开始

    语句内容  //逻辑、语句都写在此处

  end  //标识着存储过程逻辑的结束



DECLARE 声明变量,



DECLARE ids VARCHAR(255) DEFAULT '0';    //DECLARE 表示声明一个变量,该变量类型为varchar(255),默认值为0



把变量都定义好后,就可以使用sql语句对数据进行操作

在几个select语句之后,会看到if的判断语句,在if中使用update更新表的permissiom_path的值



if ids <> '0' THEN    //if语句的开头,if 判断条件 then
  逻辑内容
end if;    //if语句的结尾



while循环语句,用于循环遍历同级数据,在这个存储过程中也充当条件,避免无限递归循环



while i <= IND  DO  //while语句的开头,while 判断条件 do
  逻辑内容
end while;    //while语句的结尾



存储过程的递归调用有两个核心:

1、设置树的极限深度(由于mysql数据库的存储过程调用中树的深度默认为0,即不建议存储过程调用存储过程,所以需要这样的设置)



SET @@max_sp_recursion_depth = 100;   //绝大部分情况设置100就够用了,除非递归调用的深度超过100



去掉则会报错:



Recursive limit 0 (as set by the max_sp_recursion_depth variable)



2、调用自己



call test(substring_index(substring_index(ids,',', i), ',', -1));  //call 存储过程名(参数)



最后,由于我的permission表的树形结构的根节点id是0,所以我在生成存储过程后执行的语句为



call test(0)



这样就可以为permision表中的permission_path字段自动赋上路径值

其实存储过程的用法并不复杂,只要记住格式就行了,固定的格式开头和结尾,中间的逻辑都可以是非常简单的语句。而存储过程的递归调用需要注意的地方无非是树的深度的设置,以及防止死循环的逻辑。对于用代码写过递归调用的朋友们来说这不是什么有难度的事,如果没有写过递归调用这样的逻辑,建议先通过代码熟悉递归调用的特点以及注意事项,再来写sql!

ps:第一次写博客,如有不足的地方欢迎指出!