☆【嚣张恒少】mysql实现oracle start with connect by递归

一、测试数据准备:
create table treeList(
id varchar(10), – 节点ID
name varchar(10), – 节点名称
pId varchar(10) – 父ID
);

insert into treeList values(1,‘中国’,null);
insert into treeList values(2,‘北京’,1);
insert into treeList values(3,‘上海’,1);
insert into treeList values(4,‘深圳’,1);
insert into treeList values(5,‘海淀’,2);
insert into treeList values(6,‘朝阳’,2);
insert into treeList values(7,‘昌平’,2);
insert into treeList values(8,‘丰台’,2);

mysql> select * from treelist;
±-----±-------±-----+
| id | name | pId |
±-----±-------±-----+
| 1 | 中国 | NULL |
| 2 | 北京 | 1 |
| 3 | 上海 | 1 |
| 4 | 深圳 | 1 |
| 5 | 海淀 | 2 |
| 6 | 朝阳 | 2 |
| 7 | 昌平 | 2 |
| 8 | 丰台 | 2 |
±-----±-------±-----+
8 rows in set (0.07 sec)

二、观察oracle start with connect by使用效果:
Select
t.Id,t.Name,t.PID
From treeList t
Start With t.pId=1
Connect By Prior t.Id = t.PId;
1 中国
2 北京 1
5 海淀 2
6 朝阳 2
7 昌平 2
8 丰台 2
3 上海 1
4 深圳 1

Select
t.Id,t.Name,t.PID
From treeList t
Start With t.pId=2
Connect By Prior t.Id = t.PId;
5 海淀 2
6 朝阳 2
7 昌平 2
8 丰台 2

Select
t.Id,t.Name,t.PID
From treeList t
Start With t.Id=2
Connect By Prior t.Id = t.PId;
2 北京 1
5 海淀 2
6 朝阳 2
7 昌平 2
8 丰台 2

三、mysql实现方法:
1.自定义函数
########################################################
delimiter $$

CREATE FUNCTION getChildList (rootId VARCHAR(100))
– rootId为要查询的节点
RETURNS VARCHAR(1000)
BEGIN
DECLARE pTemp VARCHAR(1000);
DECLARE cTemp VARCHAR(1000); – 定义两个临时变量
SET pTemp = ‘’;
SET cTemp = rootId;
WHILE cTemp is not null DO
if (pTemp = ‘’) then
SET pTemp = cTemp;
elseif(pTemp <> ‘’) then
SET pTemp = concat(pTemp,’,’,cTemp); – 所有节点连接成字符串
end if;
SELECT group_concat(id) INTO cTemp FROM treeList
WHERE FIND_IN_SET(pId,cTemp)>0;
END $$
delimiter ;
#############################################
mysql> select * from treelist where find_in_set(id,getchildlist(1));
±-----±-------±-----+
| id | name | pId |
±-----±-------±-----+
| 1 | 中国 | NULL |
| 2 | 北京 | 1 |
| 3 | 上海 | 1 |
| 4 | 深圳 | 1 |
| 5 | 海淀 | 2 |
| 6 | 朝阳 | 2 |
| 7 | 昌平 | 2 |
| 8 | 丰台 | 2 |
±-----±-------±-----+
8 rows in set (0.01 sec)

mysql> select * from treelist where find_in_set(id,getchildlist(2));
±-----±-------±-----+
| id | name | pId |
±-----±-------±-----+
| 2 | 北京 | 1 |
| 5 | 海淀 | 2 |
| 6 | 朝阳 | 2 |
| 7 | 昌平 | 2 |
| 8 | 丰台 | 2 |
±-----±-------±-----+
5 rows in set (0.01 sec)

2.存储过程实现
###################################################################
delimiter $$
drop procedure if exists pro_getchildlist;
CREATE PROCEDURE pro_getchildlist(IN startId varchar(200),in v_start_with char(1),
in tab_name varchar(100),in son_id varchar(100),in parent_id varchar(100),out result varchar(1500))
begin
– ***********************************************************************************************
– 功能:替代oracle start with connect by语法,递归查询
– 参数说明 : startid参数:start with 后面的字段值
– v_start_with参数:start with id:s(包含这条数据), start with parent_id:p(不包含这条数据)
– tab_name参数:目标表
– son_id:唯一标识id
– parent_id:父id
– result:最终输出所有id值,以(1,2,3,5,6…)的形式
– 使用说明 :例如 步骤1: call pro_getchildlist(2,‘p’,‘treelist’,‘id’,‘pid’,@m);
– 步骤2: select * from treelist where find_in_set(id,@m);
– 补充说明 :startId 为null不处理,太麻烦了,如果parent_id为null,直接将条件改为son_id为1即可
– 创建日期 : 2019-10-10
– 创建人 : zhangheng
– 修改历史 : 修改人 修改时间 主要改动说明
– ***********************************************************************************************
declare flag int default 1;
declare j int;
declare k int;

– start with 子ID
– 判断表里面有没有此数据,没有则为空
if v_start_with=‘s’ then
set @sql=concat(
'select count(*) from ‘,
tab_name,
’ where ‘,
son_id,
‘=’,
‘’’,
startId,
‘’’,
’ limit 1 into @j ');
prepare stmt from @sql;
execute stmt;
set j=@j;
end if;

– start with 父ID
– 判断表里面有没有此数据,没有则为空
if v_start_with=‘p’ then
set @sql=concat(
'select count(*) from ‘,
tab_name,
’ where ‘,
parent_id,
‘=’,
‘’’,
startId,
‘’’,
’ limit 1 into @k ');
prepare stmt from @sql;
execute stmt;
set k=@k;
end if;

– 表里面能够查询到数据才做处理
if j!=0 || k!=0
then

– t1 存放查询条件query
drop table if exists t1;
create temporary table t1(
id varchar(32) not null,
pid varchar(32),
key idx_1(id),
key idx_2(pid));

– t2 存放最终结果集result
drop table if exists t2;
create temporary table t2(
id varchar(32) not null,
pid varchar(32),
key idx_1(id),
key idx_2(pid));

– t3 中间表倒腾数据用temp
drop table if exists t3;
create temporary table t3(
id varchar(32) not null,
pid varchar(32),
key idx_1(id),
key idx_2(pid));

if v_start_with=‘p’ then
set @sql=concat('insert into t1 select ',
son_id,
‘,’,
parent_id,
’ from ‘,
tab_name,
’ where ‘,
parent_id,
‘=’,
‘’’,
startId,
‘’’);
prepare stmt from @sql;
execute stmt;

set @sql=concat('insert into t2 select ',
son_id,
‘,’,
parent_id,
’ from ‘,
tab_name,
’ where ‘,
parent_id,
‘=’,
‘’’,
startId,
‘’’);
prepare stmt from @sql;
execute stmt;

– insert into t1(id,pid) select id,pid from treelist where parent_id=startId;
– insert into t2(id,pid) select id,pid from treelist where parent_id=startId;

elseif v_start_with=‘s’ then

set @sql=concat('insert into t1 select ',
son_id,
‘,’,
parent_id,
’ from ‘,
tab_name,
’ where ‘,
parent_id,
‘=’,
‘’’,
startId,
‘’’);
prepare stmt from @sql;
execute stmt;

set @sql=concat('insert into t2 select ',
son_id,
‘,’,
parent_id,
’ from ‘,
tab_name,
’ where ‘,
parent_id,
‘=’,
‘’’,
startId,
‘’’);
prepare stmt from @sql;
execute stmt;

set @sql=concat('insert into t2 select ',
son_id,
‘,’,
parent_id,
’ from ‘,
tab_name,
’ where ‘,
son_id,
‘=’,
‘’’,
startId,
‘’’);
prepare stmt from @sql;
execute stmt;

– insert into t1(id,pid) select id,pid from treelist where parent_id=startId;
– insert into t2(id,pid) select id,pid from treelist where sonid=startId;
– insert into t2(id,pid) select id,pid from treelist where parent_id=startId;

end if;

– 循环开始
while flag>0 do
truncate t3;

– insert into t3(id,pid)
– select A.id,A.pid
– from treelist A,t1
– where A.pid=t1.id;

– 将查询条件表t1与源表关联到的数据存到中间表t3
set @sql=concat(
‘insert into t3(id,pid) select A.’,
son_id,
‘,A.’,
parent_id,
’ from ‘,
tab_name,
’ A,t1 where A.’,
parent_id,
‘=t1.id’);
prepare stmt from @sql;
execute stmt;

truncate t1;
– 将中间表t3数据插入到查询条件表t1作为下一次循环使用
insert into t1 select id,pid from t3;

– 将中间表t3数据插入到结果集表t2
insert into t2 select id,pid from t3;

truncate t3;

– select count(1) from
– treelist A, t1
– where A.pid=t1.id into flag;

– 判断是否还能关联到数据,直到关联不到为止
set @sql=concat(
'select count(1) from ‘,
tab_name,
’ A,t1 where A.’,
parent_id,
‘=t1.id into @flag’
);
prepare stmt from @sql;
execute stmt;
set flag=@flag;

end while;

select group_concat(id) from t2 into result;

– 删除临时表
drop TEMPORARY table t1;
drop TEMPORARY table t2;
drop TEMPORARY table t3;

end if;

end $$
delimiter ;
#########################################################

#########################################################
delimiter $$
drop procedure if exists pro_getparentlist;
CREATE PROCEDURE pro_getparentlist(IN startId varchar(200),in v_start_with char(1),
in tab_name varchar(100),in son_id varchar(100),in parent_id varchar(100),out result varchar(1500))
begin
– ***********************************************************************************************
– 功能:替代oracle start with connect by语法,递归查询
– 参数说明 : startid参数:start with 后面的字段值
– v_start_with参数:start with id:p(包含这一堆数据), start with parent_id:s(不包含这一堆数据)
– tab_name参数:目标表
– son_id:唯一标识id
– parent_id:父id
– result:最终输出所有id值,以(1,2,3,5,6…)的形式
– 使用说明 :例如 步骤1: call pro_getparentlist(2,‘p’,‘treelist’,‘id’,‘pid’,@m);
– 步骤2: select * from treelist where find_in_set(id,@m);
– 补充说明 :startId 为null不处理,太麻烦了,如果parent_id为null,直接将条件改为son_id为1即可
– 创建日期 : 2019-10-10
– 创建人 : zhangheng
– 修改历史 : 修改人 修改时间 主要改动说明
– ***********************************************************************************************
declare flag int default 1;
declare j int;
declare k int;

– start with 子ID
– 判断表里面有没有此数据,没有则为空
if v_start_with=‘s’ then
set @sql=concat(
'select count(*) from ‘,
tab_name,
’ where ‘,
son_id,
‘=’,
‘’’,
startId,
‘’’,
’ limit 1 into @j ');
prepare stmt from @sql;
execute stmt;
set j=@j;
end if;

– start with 父ID
– 判断表里面有没有此数据,没有则为空
if v_start_with=‘p’ then
set @sql=concat(
'select count(*) from ‘,
tab_name,
’ where ‘,
parent_id,
‘=’,
‘’’,
startId,
‘’’,
’ limit 1 into @k ');
prepare stmt from @sql;
execute stmt;
set k=@k;
end if;

– 表里面能够查询到数据才做处理
if j!=0 || k!=0
then

– t1 存放查询条件query
drop table if exists t1;
create temporary table t1(
id varchar(32) not null,
pid varchar(32),
key idx_1(id),
key idx_2(pid));

– t2 存放最终结果集result
drop table if exists t2;
create temporary table t2(
id varchar(32) not null,
pid varchar(32),
key idx_1(id),
key idx_2(pid));

– t3 中间表倒腾数据用temp
drop table if exists t3;
create temporary table t3(
id varchar(32) not null,
pid varchar(32),
key idx_1(id),
key idx_2(pid));

if v_start_with=‘p’ then
set @sql=concat('insert into t1 select ',
son_id,
‘,’,
parent_id,
’ from ‘,
tab_name,
’ where ‘,
son_id,
‘=’,
‘’’,
startId,
‘’’);
prepare stmt from @sql;
execute stmt;

set @sql=concat('insert into t2 select ',
son_id,
‘,’,
parent_id,
’ from ‘,
tab_name,
’ where ‘,
son_id,
‘=’,
‘’’,
startId,
‘’’);
prepare stmt from @sql;
execute stmt;

set @sql=concat('insert into t2 select ',
son_id,
‘,’,
parent_id,
’ from ‘,
tab_name,
’ where ‘,
parent_id,
‘=’,
‘’’,
startId,
‘’’);
prepare stmt from @sql;
execute stmt;

– insert into t1(id,pid) select id,pid from treelist where id=startId;
– insert into t2(id,pid) select id,pid from treelist where id=startId;
– insert into t2(id,pid) select id,pid from treelist where parent_id=startId;

elseif v_start_with=‘s’ then

set @sql=concat('insert into t1 select ',
son_id,
‘,’,
parent_id,
’ from ‘,
tab_name,
’ where ‘,
son_id,
‘=’,
‘’’,
startId,
‘’’);
prepare stmt from @sql;
execute stmt;

set @sql=concat('insert into t2 select ',
son_id,
‘,’,
parent_id,
’ from ‘,
tab_name,
’ where ‘,
son_id,
‘=’,
‘’’,
startId,
‘’’);
prepare stmt from @sql;
execute stmt;

– insert into t1(id,pid) select id,pid from treelist where id=startId;
– insert into t2(id,pid) select id,pid from treelist where id=startId;

end if;

– 循环开始
while flag>0 do
truncate t3;

– insert into t3(id,pid)
– select A.id,A.pid
– from treelist A,t1
– where A.pid=t1.id;

– 将查询条件表t1与源表关联到的数据存到中间表t3
set @sql=concat(
‘insert into t3(id,pid) select A.’,
son_id,
‘,A.’,
parent_id,
’ from ‘,
tab_name,
’ A,t1 where A.’,
son_id,
‘=t1.pid’);
prepare stmt from @sql;
execute stmt;

truncate t1;
– 将中间表t3数据插入到查询条件表t1作为下一次循环使用
insert into t1 select id,pid from t3;

– 将中间表t3数据插入到结果集表t2
insert into t2 select id,pid from t3;

truncate t3;

– select count(1) from
– treelist A, t1
– where A.pid=t1.id into flag;

– 判断是否还能关联到数据,直到关联不到为止
set @sql=concat(
'select count(1) from ‘,
tab_name,
’ A,t1 where A.’,
son_id,
‘=t1.pid into @flag’
);
prepare stmt from @sql;
execute stmt;
set flag=@flag;

end while;

select group_concat(id) from t2 into result;

– 删除临时表
drop TEMPORARY table t1;
drop TEMPORARY table t2;
drop TEMPORARY table t3;

end if;

end $$
delimiter ;
###########################################################
使用方法:
mysql> call pro_getparentlist(2,‘p’,‘treelist’,‘id’,‘pid’,@m);
Query OK, 0 rows affected (0.32 sec)

mysql> select @m;
±------------+
| @m |
±------------+
| 1,2,5,6,7,8 |
±------------+
1 row in set (0.00 sec)

mysql> select * from treelist where find_in_set(id,@m);
±-----±-------±-----+
| id | name | pId |
±-----±-------±-----+
| 1 | 中国 | NULL |
| 2 | 北京 | 1 |
| 5 | 海淀 | 2 |
| 6 | 朝阳 | 2 |
| 7 | 昌平 | 2 |
| 8 | 丰台 | 2 |
±-----±-------±-----+
6 rows in set (0.04 sec)

mysql> call pro_getparentlist(2,‘s’,‘treelist’,‘id’,‘pid’,@m);
Query OK, 0 rows affected (0.11 sec)

mysql> select @m;
±-----+
| @m |
±-----+
| 1,2 |
±-----+
1 row in set (0.00 sec)

mysql> select * from treelist where find_in_set(id,@m);
±-----±-------±-----+
| id | name | pId |
±-----±-------±-----+
| 1 | 中国 | NULL |
| 2 | 北京 | 1 |
±-----±-------±-----+
2 rows in set (0.00 sec)

mysql> call pro_getchildlist(2,‘s’,‘treelist’,‘id’,‘pid’,@m);
Query OK, 0 rows affected (0.14 sec)

mysql> select @m;
±----------+
| @m |
±----------+
| 2,5,6,7,8 |
±----------+
1 row in set (0.00 sec)

mysql> select * from treelist where find_in_set(id,@m);
±-----±-------±-----+
| id | name | pId |
±-----±-------±-----+
| 2 | 北京 | 1 |
| 5 | 海淀 | 2 |
| 6 | 朝阳 | 2 |
| 7 | 昌平 | 2 |
| 8 | 丰台 | 2 |
±-----±-------±-----+
5 rows in set (0.00 sec)

mysql> call pro_getchildlist(2,‘p’,‘treelist’,‘id’,‘pid’,@m);
Query OK, 0 rows affected (0.16 sec)

mysql> select @m;
±--------+
| @m |
±--------+
| 5,6,7,8 |
±--------+
1 row in set (0.00 sec)

mysql> select * from treelist where find_in_set(id,@m);
±-----±-------±-----+
| id | name | pId |
±-----±-------±-----+
| 5 | 海淀 | 2 |
| 6 | 朝阳 | 2 |
| 7 | 昌平 | 2 |
| 8 | 丰台 | 2 |
±-----±-------±-----+
4 rows in set (0.00 sec)

参考博客:
https://www.jianshu.com/p/6881b9ec1759
https://www.cnblogs.com/hjw-zq/p/8820810.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值