TSQL脚本能实现递归查询,用户使用共用表表达式 CTE(Common Table Expression),只需要编写少量的代码,就能实现递归查询。

本文详细介绍CTE递归调用的特性和使用示例,递归查询主要用于层次结构的查询,从叶级(Leaf Level)向顶层(Root Level)查询,或从顶层向叶级查询,或递归的路径(Path)。

一、递归查询原理

递归调用是指自己调用自己,使用CTE实现递归查询必须满足三个条件:初始条件,递归调用表达式,终止条件。

CTE 递归查询的伪代码如下:

WITH  recursive  cte_name ( column_name [,...n]) AS (
CTE_query_definition
UNION ALL
CTE_query_definition
)
SELECT * FROM cte_name

1,递归查询至少包含两个子查询:

第一个子查询称作定点(Anchor)子查询:定点查询只是一个返回有效表的查询,用于设置递归的初始值;

第二个子查询称作递归子查询:该子查询调用CTE名称,触发递归查询,实际上是递归子查询调用递归子查询;

两个子查询使用union all,求并集;

2,CTE的递归终止条件

递归查询没有显式的递归终止条件,只有当递归子查询返回空结果集(没有数据行返回)或是超出了递归次数的最大限制时,才停止递归。

默认的递归查询次数是100,可以使用查询提示(hint):MAXRECURSION 控制递归的最大次数:OPTION( MAXRECURSION 16);如果允许无限制的递归次数,使用查询提示:option(maxrecursion 0);当递归查询达到指定或默认的 MAXRECURSION 数量限制时,SQL Server将结束查询并返回错误,如下:

The statement terminated. The maximum recursion 10 has been exhausted before statement completion.

事务执行失败,该事务包含的所有操作都被回滚。在产品环境中,慎用maxrecursion 查询提示,推荐通过 where 条件限制递归的次数。

3,递归步骤

step1:定点子查询设置CTE的初始值,即CTE的初始值Set0;

递归调用的子查询过程:递归子查询调用递归子查询;

step2:递归子查询第一次调用CTE名称,CTE名称是指CTE的初始值Set0,第一次执行递归子查询之后,CTE名称是指结果集Set1;

step3:递归子查询第二次调用CTE名称,CTE名称是指Set1,第二次执行递归子查询之后,CTE名称是指结果集Set2;

step4:在第N次执行递归子查询时,CTE名称是指Set(N-1),递归子查询都引用前一个递归子查询的结果集;

Step5:如果递归子查询返回空数据行,或超出递归次数的最大限制,停止递归;

二、递归查询演示

创建表:

CREATE TABLE `test_child_parent` (
`id` int NOT NULL,
`name` varchar(255) DEFAULT NULL,
`parent_id` int DEFAULT NULL,
`parent_name` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

插入数据:

INSERT INTO `test_child_parent`(`id`, `name`, `parent_id`, `parent_name`) VALUES (1, '一级', NULL, NULL);
INSERT INTO `test_child_parent`(`id`, `name`, `parent_id`, `parent_name`) VALUES (2, '二级', 1, '一级');
INSERT INTO `test_child_parent`(`id`, `name`, `parent_id`, `parent_name`) VALUES (3, '二级', 1, '一级');
INSERT INTO `test_child_parent`(`id`, `name`, `parent_id`, `parent_name`) VALUES (4, '三级', 2, '二级');
INSERT INTO `test_child_parent`(`id`, `name`, `parent_id`, `parent_name`) VALUES (5, '四级', 3, '二级');
INSERT INTO `test_child_parent`(`id`, `name`, `parent_id`, `parent_name`) VALUES (6, '五级', 5, '四级');
INSERT INTO `test_child_parent`(`id`, `name`, `parent_id`, `parent_name`) VALUES (7, '五级', 5, '四级');
INSERT INTO `test_child_parent`(`id`, `name`, `parent_id`, `parent_name`) VALUES (8, '五级', 5, '四级');

查询数据:

select  *  from  test_child_parent ;

向上递归查询

 with recursive temp as (
select * from test_child_parent p where id = 8
union all
select t.* from test_child_parent t inner join temp t2 on t2.parent_id = t.id
)
select * from temp;

查询结果:

向下递归:

--查询下游节点(包含自己)
with recursive temp as (
select * from test_child_parent p where id = 3
union all
select t.* from test_child_parent t inner join temp t2 on t2.id = t.parent_id
)
select * from temp ;

查询结果:

、特殊情况(死循环)

如果数据出现异常,比如 A -> B , B -> C , C -> A ,出现这样的情况,查询时就会出现死循环。

INSERT INTO `test_child_parent`(`id`, `name`, `parent_id`, `parent_name`) VALUES (3, '二级', 8, '五级');

这样再查询时就会报错:

Recursive query aborted after 1001 iterations. Try increasing @@cte_max_recursion_depth to a larger value.

遇到这种死循环的递归查询,如何避免呢?

在MYSQL 8.109 引入了 LIMIT 语句,通过LIMIT 来限制输出数据的数量。

 with recursive temp as (
select * from test_child_parent p where id = 8
union all
select t.* from test_child_parent t inner join temp t2 on t2.parent_id = t.id limit 10
)
select * from temp ;

查询结果:

 此时可以很快查询出结果,但是存在如下问题:

1、limit 返回行数,需要合理设置,否则返回的数据可以缺少;

2、查询的结果是重复的,需要进行去重处理,比如:在查询时加上distinct,或者使用group  by 等方式。

 with recursive temp as (
select * from test_child_parent p where id = 8
union all
select t.* from test_child_parent t inner join temp t2 on t2.parent_id = t.id limit 1000
)
select distinct * from temp ;

MySQL8.0-CTE递归查询(避免死循环)的更多相关文章

  1. Mysql8.0新特性【详细版本】

    1.  账户与安全 用户创建与授权 之前:创建用户并授权 1 grant all privileges on *.* to 'myuser'@'%' identified by '3edc#EDC'; ...

  2. SQL Server 2005中的CTE递归查询得到一棵树

    感觉这个CTE递归查询蛮好用的,先举个例子: use City; go create table Tree ( ID int identity(1,1) primary key not null, N ...

  3. SQL Server CTE 递归查询全解

    在TSQL脚本中,也能实现递归查询,SQL Server提供CTE(Common Table Expression),只需要编写少量的代码,就能实现递归查询,本文详细介绍CTE递归调用的特性和使用示例 ...

  4. SQL Server CTE 递归查询全解 -- 转 学习

    在TSQL脚本中,也能实现递归查询,SQL Server提供CTE(Common Table Expression),只需要编写少量的代码,就能实现递归查询,本文详细介绍CTE递归调用的特性和使用示例 ...

  5. SQL Server CTE 递归查询全解(转载)

    在TSQL脚本中,也能实现递归查询,SQL Server提供CTE(Common Table Expression),只需要编写少量的代码,就能实现递归查询,本文详细介绍CTE递归调用的特性和使用示例 ...

  6. CTE 递归查询全解

    TSQL脚本能实现递归查询,用户使用共用表表达式 CTE(Common Table Expression),只需要编写少量的代码,就能实现递归查询.本文详细介绍CTE递归调用的特性和使用示例,递归查询 ...

  7. SQLServer2005中的CTE递归查询得到一棵树

    最近研究了一下CTE递归查询,感觉这个CTE递归查询蛮好用的,在网上找到了一个比较好的例子,测试例子如下 use City; go create table Tree ( ID ,) primary ...

  8. 利用临时表实现CTE递归查询

    一.CTE递归查询简介 --CTE递归查询终止条件在TSQL脚本中,也能实现递归查询,SQL Server提供CTE(Common Table Expression),只需要编写少量的代码,就能实现递 ...

  9. MySQL8.0新特性实验1

    Server层,选项持久化 mysql> show variables like '%max_connections%';+------------------------+-------+| ...

  10. 跨时代的MySQL8.0新特性解读

    目录 MySQL发展历程 MySQL8.0新特性 秒级加列 性能提升 文档数据库 SQL增强 共用表表达式(CTEs) 不可见索引(Invisible Indexes) 降序索引(Descending ...

随机推荐

  1. 改变函数内this指向方法——call、apply、bind

    javascript为我们专门提供了一些函数方法来帮我们更优雅的处理函数内部this的指向问题,常用的有bind( ).call( ).apply( )三种方法. 相同点: 都可以改变函数内部的thi ...

  2. poj 1426 深搜

    ***可能有多个答案,DFS一下找出一个答案即可*** #include<stdio.h> #include<string.h> #include<stdlib.h> ...

  3. CDC设计实例-01

    CDC设计实例 Clock Gating Cell & Glitch Free Clock Switch(门控单元和动态切换时钟) 一个电路有多个时钟输入进来,希望在工作当中能够动态切换时钟; ...

  4. 【ARM】为堆和栈保留空的内存块

    此示例演示如何使用分散加载描述为堆栈和堆保留和清空内存块.它还显示链接器生成的相关符号. 在以下示例中,执行区域定义STACK 0x800000 EMPTY -0x10000定义了一个名为STACK ...

  5. SpringMVC08——拦截器——2021-05-11

    拦截器概述 SpringMVC的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并做相应的处理. 例如:进行权限验证.记录请求信息的日志.判断用户 ...

  6. 【Mysql系列】(一)MySQL语句执行流程

    首发博客地址 首发博客地址 系列文章地址 参考文章 MySQL 逻辑架构 连接器 连接命令一般是这么写的 mysql -h$ip -P$port -u$user -p 那么 什么是连接器? MySQL ...

  7. [转帖]《Linux性能优化实战》笔记(六)—— Linux 软中断与对应故障分析方法

    中断是系统用来响应硬件设备请求的一种机制,它会打断进程的正常调度和执行,然后调用内核中的中断处理程序来响应设备的请求. 一. 为什么要有中断 举个生活中的例子,让你感受一下中断的魅力.比如说你订了一份 ...

  8. Debian 安装vim 提示版本问题的处理

    https://blog.csdn.net/Oil__/article/details/113384278 purge 还有 --allow-remove-essential 安装失败提示解决方法安装 ...

  9. 查看dmesg 里面部分内容的精确时间

    for i in `dmesg |grep "stuck for" |awk '{print $1}' |awk -F "." '{print $1}' |aw ...

  10. 手写promise实现自定义封装多个回调函数的执行

    自定义封装多个回调函数的执行 <script src="./Promise.js"></script> let p = new Promise((resol ...