最近在做表数据整理的时候碰到这样的一个问题,我有一张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:第一次写博客,如有不足的地方欢迎指出!