mysql with recursive 递归用法

时间:2023-02-04 11:57:14


with recursive 是一个递归的查询子句,他会把查询出来的结果再次代入到查询子句中继续查询。

语法:

WITH RECURSIVE  cte_name  AS ( 
初始语句(非递归部分)
UNION ALL
递归部分语句
)
[ SELECT| INSERT | UPDATE | DELETE]

例子1:

WITH RECURSIVE cte (n) AS
(
SELECT 1
UNION ALL
SELECT n + 1 FROM cte WHERE n < 5
)
SELECT * FROM cte;

mysql with recursive 递归用法


该语句书写包括如下几步

  1. 设定递归语法,首先初始执行第一句​​SELECT 1​​​,也可以写成​​select xxx from xxx where xxx​
  2. 其结果给到n,当n值发生改变,就会执行:​​SELECT n + 1 FROM cte WHERE n < 5​
  3. 最终结果给到n输出

注意:WITH AS () 后面必须跟着 [ SELECT| INSERT | UPDATE | DELETE] 语句,否则报错。

例子2:

有如下行政区划表

create table administrative_division2
(
administrative_division_sn bigint not null comment '行政区划代码'
primary key,
parent_administrative_division_sn bigint null comment '上级行政区划代码',
status tinyint default 1 not null comment '状态',
name varchar(28) not null comment '名称*',
full_name varchar(200) null comment '行政区划全路径名称,省市县全路径名称'
)
comment '行政区划';

INSERT INTO administrative_division2 (administrative_division_sn, parent_administrative_division_sn, status, name, full_name) VALUES (110000, null, 1, '北京市', '');
INSERT INTO administrative_division2 (administrative_division_sn, parent_administrative_division_sn, status, name, full_name) VALUES (110100, 110000, 1, '北京市市辖区', '');
INSERT INTO administrative_division2 (administrative_division_sn, parent_administrative_division_sn, status, name, full_name) VALUES (110101, 110100, 1, '东城区', '');
INSERT INTO administrative_division2 (administrative_division_sn, parent_administrative_division_sn, status, name, full_name) VALUES (110102, 110100, 1, '西城区', '');
INSERT INTO administrative_division2 (administrative_division_sn, parent_administrative_division_sn, status, name, full_name) VALUES (110105, 110100, 1, '朝阳区', '');

mysql with recursive 递归用法


我们想补充​​full_name​​的值变成如下

mysql with recursive 递归用法


那么可以用如下语句

update administrative_division2 ad,(
with recursive res as (select administrative_division_sn sn, parent_administrative_division_sn pSn, name
from administrative_division2 ad
where parent_administrative_division_sn is null
union
select ad2.administrative_division_sn as sn,
ad2.parent_administrative_division_sn pSn,
concat(res.name, ad2.name) as name
from res
inner join administrative_division2 ad2
on res.sn = ad2.parent_administrative_division_sn)
select * from res
) as t1 set ad.full_name=t1.name where ad.administrative_division_sn=t1.sn;

该语句执行解析如下

  1. 先设定开始的条件为父ID为空,即所有的父节点
select administrative_division_sn sn, parent_administrative_division_sn pSn, name from administrative_division2 ad where parent_administrative_division_sn is null
  1. 设置循环的条件为等于这个父节点的子节点
select ad2.administrative_division_sn as sn, ad2.parent_administrative_division_sn pSn, concat(res.name, ad2.name) as name from res inner join administrative_division2 ad2 on res.sn = ad2.parent_administrative_division_sn
  1. 执行完2此时的res.sn就等于子sn了,然后递归执行2
  2. 最终执行​​set ad.full_name=t1.name where ad.administrative_division_sn=t1.sn​​​把名称赋值为2中的拼接了的名称​​concat(res.name, ad2.name)​