learn and grow up

自己写一个mysql动态查询树形结构的存储过程

字数统计: 1.2k阅读时长: 6 min
2016/09/09 Share

写在前面

这是我三年前记录在自己csdn博客上的,觉得很有用,搬过来~

正文

mysql不像oracle有start with … connect by …这种递归查询树形结构的语句,但是日常开发中会经常查询树形数据,所以自己就摸索了一套可以动态传参来递归查询树形结构的数据

调用示例:

调用示例

参数

  • schema_name 库名
  • table_name 表名
  • type :1、父级2、子级
  • self_value: 节点值
  • id_name:节点主键id,用于查找子节点
  • self_name:本节点的字段名称,用于找父级
  • parent_name:父节点名称,用于找子级和父级
  • rt_value:返回值,MEDIUMTEXT,最大16777215(2^24-1)

返回结果

  • rt_value 所有节点id拼起来的、以,号分隔的字符串

mysql源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#辅助方法
CREATE FUNCTION `func_get_split_string`(`f_string` varchar(1000), `f_delimiter` varchar(5), `f_order` int)
RETURNS varchar(255) CHARSET utf8
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
#592876
#f_string字符串按f_delimiter截取后获取第f_order位
declare result varchar(255) default '';
declare oneSub varchar(255) default '';
declare cou int default 0;

set cou=1+(length(f_string) - length(replace(f_string,f_delimiter,'')));


if f_order>cou then
return result;
end if;
set result = reverse(substring_index(reverse(substring_index(f_string,f_delimiter,f_order)),f_delimiter,1));
return result;
END
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#子存储过程
CREATE PROCEDURE `pro_find_tree_recursion`(IN `table_name` VARCHAR(50), IN `parent_name` VARCHAR(50), IN `id_name` VARCHAR(50), IN `self_value` VARCHAR(50), INOUT `rt_value` MEDIUMTEXT)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
recur:BEGIN
DECLARE done INT DEFAULT 0;
DECLARE b VARCHAR (32);
DECLARE all_value VARCHAR (1000);
DECLARE tree_sql1 VARCHAR (1000);
DECLARE cou INT default 0;
DECLARE ALL_VALUE_COU INT default 0;
DECLARE one_value VARCHAR (100);
DECLARE max_loop INT DEFAULT 0;
DECLARE now_loop INT DEFAULT 0;




set @@session.max_sp_recursion_depth=20;

#现将自己塞进来
if length(rt_value)=0 or rt_value is null
then
set rt_value=concat(self_value);
else
set rt_value=concat(rt_value,';',self_value);
end if;


#查找出自己所有的下级
set tree_sql1= concat('select GROUP_CONCAT(',id_name,') into @l_value ',/*,id_name,' into @l_id','*/'from ',table_name,' t where t.',parent_name,' = ',self_value);
#select tree_sql1;
set @tree_sql1=tree_sql1;
prepare stmt from @tree_sql1;
EXECUTE stmt;
deallocate prepare stmt;
set all_value=@l_value;

#如果没有下级,返回上一层
if length(all_value)=0 or all_value is null
then
leave recur;
end if;



#如果有下级,则继续循环
rec_while :while 1=1
do
set now_loop=now_loop+1;
#拼接符设置
set one_value=func_get_split_string(all_value,',',now_loop);
if length(one_value)=0 or one_value is null then
#下级循环结束,返回上一层
leave rec_while;
end if;
#继续找下级的子节点
call pro_find_tree_recursion(table_name,parent_name,id_name,one_value,rt_value);
end while;

leave recur;
END
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#主存储过程
CREATE PROCEDURE `pro_find_tree_value`(IN `schema_name` VARCHAR(50), IN `table_name` VARCHAR(50), IN `type` INT, IN `self_value` VARCHAR(50), IN `id_name` VARCHAR(50), IN `self_name` VARCHAR(50), IN `parent_name` VARCHAR(50), IN `recure_num` INT, INOUT `rt_value` MEDIUMTEXT)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
pro_begin:BEGIN

DECLARE _value varchar(100);
DECLARE rtValue text default '';
DECLARE _schema varchar(100);
DECLARE cou int;
DECLARE tree_sql1 VARCHAR(500);
DECLARE id_value varchar(100);


if length(schema_name)=0
then select database() into _schema;
else set _schema=schema_name ;
end if;



select count(1) into cou
from information_schema.columns a where a.table_schema = _schema and a.table_name= table_name
and a.column_name=self_name;
if cou=0

then leave pro_begin;
end if;

select count(1) into cou
from information_schema.columns a where a.table_schema = _schema and a.table_name= table_name
and a.column_name=parent_name;
if cou=0
then leave pro_begin;
end if;

set tree_sql1= concat('select count(1) into @l_value from ',table_name,' t where t.',self_name,' = ',self_value);
select tree_sql1;
set @tree_sql1=tree_sql1; ##注意很重要,将连成成的字符串赋值给一个变量(可以之前没有定义,但要以@开头)
prepare stmt from @tree_sql1; ##预处理需要执行的动态SQL,其中stmt是一个变量
EXECUTE stmt; ##执行SQL语句
deallocate prepare stmt; ##释放掉预处理段
set cou=@l_value;
select cou;
if cou=0
then leave pro_begin;
end if;

#type =1
#table_name的self_name值为self_value,返回parent_name的上级的上级的上级....(用分号隔开)
if type =1 then
ontTypeLoop:loop
set tree_sql1= concat('select ',parent_name,' into @l_value ',/*,id_name,' into @l_id','*/'from ',table_name,' t where t.',self_name,' = ',self_value);
#select tree_sql1;
set @tree_sql1=tree_sql1; ##注意很重要,将连成成的字符串赋值给一个变量(可以之前没有定义,但要以@开头)
prepare stmt from @tree_sql1; ##预处理需要执行的动态SQL,其中stmt是一个变量
EXECUTE stmt; ##执行SQL语句
deallocate prepare stmt; ##释放掉预处理段

if length(rt_value)=0 or rt_value is null
then
set rt_value=concat(@l_value);
else
set rt_value=concat(rt_value,';',@l_value);
end if;


set self_value=@l_value;

set tree_sql1= concat('select count(1) into @l_value from ',table_name,' t where t.',self_name,' = ',self_value);
set @tree_sql1=tree_sql1;
prepare stmt from @tree_sql1;
EXECUTE stmt;
deallocate prepare stmt;
set cou=@l_value;
if cou=0
then leave ontTypeLoop;
end if;
end loop;

end if;

#type =2
#树的子节点查询:根节点值:self_value,上级节点名:parent_name,节点id名:id_name
#利用递归返回table_name的parent_name值为self_value的下级的下级的下级....
if type=2 then
select 1;
call pro_find_tree_recursion(table_name,parent_name,id_name,self_value,rt_value);

end if;

leave pro_begin;
END
CATALOG
  1. 1. 写在前面
  2. 2. 正文