MySql8.0公共表表达式『CTE』

CTE是『common table expression』的缩写,中文翻译过来就是『公共表表达式』,使用它可以为临时查询结果命名,命名后可以在后续的查询语句中反复引用。CTE完整语法格式如下:

WITH [RECURSIVE]cte_name [(column_list)] AS (subquery)[, cte_name [(column_list)] AS (subquery)] ...
SELECT ... FROM cte_name ...;

『RECURSIVE』是递归的意思,但是它可选,表示CTE有两种模式:普通CTE和递归CTE。

官方文档:https://dev.mysql.com/doc/refman/8.0/en/with.html#common-table-expressions-recursive-examples

一、普通CTE

普通CTE写法是最常用的写法,一个经典的写法如下所示:

WITHcte1 AS (SELECT a, b FROM table1),cte2 AS (SELECT c, d FROM table2)
SELECT b, d FROM cte1 JOIN cte2
WHERE cte1.a = cte2.c;

这个没什么好说的,非常简单,需要注意的是,不只是可以写select语句,还可以写update、delete语句:

WITH ... SELECT ...
WITH ... UPDATE ...
WITH ... DELETE ...

二、递归CTE

递归CTE是一种在查询中引用自身的写法,是处理层次结构或树形数据的强大工具。其完整语法如下所示:

WITH RECURSIVE cte_name [(column_list)] AS (-- 初始化查询提供初始结果集SELECT initial_columnsFROM initial_tableWHERE initial_conditionUNION [ALL | DISTINCT]-- 递归部分SELECT recursive_columnsFROM recursive_tableJOIN cte_name ON join_conditionWHERE recursive_condition
)
SELECT * FROM cte_name [OPTIONAL_CLAUSES];

递归CTE的子查询分为两部分,通过UNION [ALL | DISTINCT]连接:

SELECT ...      -- 非递归select语句,提供初始结果集,不引用CTE名字
UNION [ALL | DISTINCT]
SELECT ...      -- 递归select语句,引用CTE名字

需要注意的是基础部分和递归部分是通过UNION [ALL | DISTINCT]连接的,虽然语法上允许UNION DISTINCT去重,但是实际上几乎都是使用UNION ALL

递归CTE语法有如下限制使用条件:

  1. 基础部分的列长度将会限制递归部分的列长度

  2. 递归select语句不能包含如下语句:聚合函数比如SUM()、窗口函数、GROUP BYorder bydistinct

  3. 递归select语句只能引用一次CTE,并且只能在from语句中使用,不能在任何子查询中使用;可以使用join语句与其它表连接,但是在这种使用场景中,CTE不能位于LEFT JOIN的右侧。

1、案例讲解

递归CTE不是很容易理解,下面通过几个案例来说明下:

案例1:打印1到5

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

输出结果:

+------+
| n    |
+------+
|    1 |
|    2 |
|    3 |
|    4 |
|    5 |
+------+

首先执行基础查询部分 SELECT 1,生成初始结果集,此时CTE的结果为:[1]

第一次递归迭代:从CTE中取出n=1,执行递归部分 SELECT n + 1 FROM cte WHERE n < 5,计算:1 + 1 = 2,,将结果2添加到CTE中,现在CTE的结果为:[1, 2]

第二次递归迭代:从CTE中取出n=2,执行递归部分,计算:2 + 1 = 3,将结果3添加到CTE中,现在CTE的结果为:[1, 2, 3]

第三次递归迭代:从CTE中取出n=3,执行递归部分,计算:3 + 1 = 4,将结果4添加到CTE中,现在CTE的结果为:[1, 2, 3, 4]

第四次递归迭代:从CTE中取出n=4,执行递归部分,计算:4 + 1 = 5,将结果5添加到CTE中,现在CTE的结果为:[1, 2, 3, 4, 5]

终止条件检查:下一次迭代时n=5,不满足WHERE条件 n < 5,递归终止。

所以,最终的查询结果就是[1, 2, 3, 4, 5]

案例2:重复字符串

在下面这个案例中,将要验证递归CTE的一个特性:基础部分的列长度将会限制递归部分的列长度。什么意思呢?看下面的sql语句:

WITH RECURSIVE cte AS
(SELECT 1 AS n, 'abc' AS strUNION ALLSELECT n + 1, CONCAT(str, str) FROM cte WHERE n < 3
)
SELECT * FROM cte;

正常来说,我们预测输出如下所示:

+------+--------------+
| n    | str          |
+------+--------------+
|    1 | abc          |
|    2 | abcabc       |
|    3 | abcabcabcabc |
+------+--------------+

但是在实际执行过程中,如果是严格模式下的运行,会提示报错:

错误代码: 1406
Data too long for column 'str' at row 1

如果是非严格模式下的运行,则会有结果如下所示:

+------+------+
| n    | str  |
+------+------+
|    1 | abc  |
|    2 | abc  |
|    3 | abc  |
+------+------+

结果总是和我们的预测不一样。这是因为在初始语句SELECT 1 AS n, 'abc' AS str中,'abc'字符串长度为3,这限制了之后的递归查询语句中的所有str列长度都是3,那么'abcabc'就存储不了了,在非严格模式下,结果会被截断,仍然是'abc';在严格模式下,则会报错。

解决方案就是在初始语句中重新定义列长度:

WITH RECURSIVE cte AS
(SELECT 1 AS n, CAST('abc' AS CHAR(20)) AS strUNION ALLSELECT n + 1, CONCAT(str, str) FROM cte WHERE n < 3
)
SELECT * FROM cte;

通过CAST('abc' AS CHAR(20)) 将列长度扩展到了20,这样就能在接下来的递归中容纳的了abcabcabc了。

案例3:斐波那契数列

斐波那契数列的定义如下:

image-20251028104635874

按照定义,前10个斐波那契额数列如下所示:0,1,1,2,3,5,8,13,21,34

写下sql打印如上斐波那契额数列:

WITH RECURSIVE cte AS 
(
SELECT 1 AS n,0 AS fn,1 AS fn_next
UNION ALL
SELECT n+1,fn_next,fn+fn_next FROM cte WHERE n<10
)
SELECT * FROM cte

案例4:日期序列生成

首先先创建表并插入数据:

-- 创建 sales 表
CREATE TABLE sales (DATE DATE,price DECIMAL(10,2)
);-- 插入数据
INSERT INTO sales (DATE, price) VALUES
('2017-01-03', 100.00),
('2017-01-03', 200.00),
('2017-01-06', 50.00),
('2017-01-08', 10.00),
('2017-01-08', 20.00),
('2017-01-08', 150.00),
('2017-01-10', 5.00);

我们现在根据日期分组统计销售额:

mysql> SELECT date, SUM(price) AS sum_priceFROM salesGROUP BY dateORDER BY date;
+------------+-----------+
| date       | sum_price |
+------------+-----------+
| 2017-01-03 |    300.00 |
| 2017-01-06 |     50.00 |
| 2017-01-08 |    180.00 |
| 2017-01-10 |      5.00 |
+------------+-----------+

虽然查询结果是对的,但是这并不是我想要的结果,我想要的结果应该是连续的日期,如果销售额为0则显示0,我想要的输出应该是这样:

+------------+-----------+
| date       | sum_price |
+------------+-----------+
| 2017-01-03 |    300.00 |
| 2017-01-04 |      0.00 |
| 2017-01-05 |      0.00 |
| 2017-01-06 |     50.00 |
| 2017-01-07 |      0.00 |
| 2017-01-08 |    180.00 |
| 2017-01-09 |      0.00 |
| 2017-01-10 |      5.00 |
+------------+-----------+

可以借助递归CTE实现该功能:

WITH RECURSIVE dates (date) AS
(SELECT MIN(date) FROM salesUNION ALLSELECT date + INTERVAL 1 DAY FROM datesWHERE date + INTERVAL 1 DAY <= (SELECT MAX(date) FROM sales)
)
SELECT dates.date, COALESCE(SUM(price), 0) AS sum_price
FROM dates LEFT JOIN sales ON dates.date = sales.date
GROUP BY dates.date
ORDER BY dates.date;

当然这个查询有些复杂,最重要的一部分是这部分sql:

WITH RECURSIVE dates (DATE) AS
(SELECT MIN(DATE) FROM salesUNION ALLSELECT DATE + INTERVAL 1 DAY FROM datesWHERE DATE + INTERVAL 1 DAY <= (SELECT MAX(DATE) FROM sales)
)
SELECT dates.date FROM dates

这部分sql生成了sales表中从最小日期到最大日期之间的所有日期列表:

+------------+
| date       |
+------------+
| 2017-01-03 |
| 2017-01-04 |
| 2017-01-05 |
| 2017-01-06 |
| 2017-01-07 |
| 2017-01-08 |
| 2017-01-09 |
| 2017-01-10 |
+------------+

然后通过左外连接sales表分组统计销售额,这样就实现了日期的连续列表。

COALESCE函数用于返回第一个不为NULL的值,在此处COALESCE(SUM(price), 0)的意思是如果没有销售额,则为0。

案例5:树状组织架构

接下来创建一个雇员表来演示树状组织架构:

CREATE TABLE employees (id         INT PRIMARY KEY NOT NULL,name       VARCHAR(100) NOT NULL,manager_id INT NULL,INDEX (manager_id),
FOREIGN KEY (manager_id) REFERENCES employees (id)
);
INSERT INTO employees VALUES
(333, "Yasmina", NULL),  # Yasmina is the CEO (manager_id is NULL)
(198, "John", 333),      # John has ID 198 and reports to 333 (Yasmina)
(692, "Tarek", 333),
(29, "Pedro", 198),
(4610, "Sarah", 29),
(72, "Pierre", 29),
(123, "Adil", 692);

执行完上述sql,表中内容如下所示:

+------+---------+------------+
| id   | name    | manager_id |
+------+---------+------------+
|   29 | Pedro   |        198 |
|   72 | Pierre  |         29 |
|  123 | Adil    |        692 |
|  198 | John    |        333 |
|  333 | Yasmina |       NULL |
|  692 | Tarek   |        333 |
| 4610 | Sarah   |         29 |
+------+---------+------------+

现在我们要写一个查询sql,用于查询每个雇员向上汇报的路径,其查询结果应当如下所示:

+------+---------+-----------------+
| id   | name    | path            |
+------+---------+-----------------+
|  333 | Yasmina | 333             |
|  198 | John    | 333,198         |
|   29 | Pedro   | 333,198,29      |
| 4610 | Sarah   | 333,198,29,4610 |
|   72 | Pierre  | 333,198,29,72   |
|  692 | Tarek   | 333,692         |
|  123 | Adil    | 333,692,123     |
+------+---------+-----------------+

查询sql如下:

WITH RECURSIVE employee_paths (id, name, path) AS
(SELECT id, name, CAST(id AS CHAR(200))FROM employeesWHERE manager_id IS NULLUNION ALLSELECT e.id, e.name, CONCAT(ep.path, ',', e.id)FROM employee_paths AS ep JOIN employees AS eON ep.id = e.manager_id
)
SELECT * FROM employee_paths ORDER BY path;

这个CTE语句从根节点(CEO)节点开始查找,查找谁的上级是CEO,然后依次递归查找,直到普通雇员为止,由于普通雇员不是谁的上级,所以JOIN语句会返回空,递归结束。

进阶问题1:查询Sarah的向上汇报路径,每个节点返回一条数据

WITH RECURSIVE employees_paths(id,NAME,manager_id) AS
(
SELECT id,NAME,manager_id FROM `employees`
WHERE NAME = 'Sarah'
UNION ALL
SELECT employees.id,employees.name,employees.manager_id FROM employees_paths 
JOIN `employees` ON employees_paths.manager_id = employees.`id`
)
SELECT * FROM employees_paths

查询结果:

    id  name     manager_id  
------  -------  ------------4610  Sarah              2929  Pedro             198198  John              333333  Yasmina        (NULL)

进阶问题2:查询John的所有下级节点

这个问题要求的是John的所有下级节点,由于子节点也有子节点,所以需要递归查询,查询sql如下:

WITH RECURSIVE employees_paths(id,NAME,manager_id) AS
(
SELECT id,NAME,manager_id FROM `employees`
WHERE NAME = 'John'
UNION ALL
SELECT employees.id,employees.name,employees.manager_id FROM employees_paths 
JOIN `employees` ON employees_paths.id = employees.`manager_id`
)
SELECT * FROM employees_paths

查询结果:

    id  NAME    manager_id  
------  ------  ------------198  John             33329  Pedro            19872  Pierre            294610  Sarah             29

这个问题的查询sql和上一个问题的查询sql非常像,只是将连接条件互换了下,其结果就完全不一样了。

2、递归失控

如果递归终止条件设置的不正确,则会导致递归失控,看下面的案例sql:

WITH RECURSIVE cte (n) AS
(SELECT 1UNION ALLSELECT n + 1 FROM cte
)
SELECT * FROM cte

在mysql中执行上述语句,会提示如下报错:

错误代码: 3636
Recursive query aborted after 1001 iterations. Try increasing @@cte_max_recursion_depth to a larger value.

这意思是递归次数超过了系统限制,拒绝执行剩余递归查询。从报错上来看,系统默认设置的递归最大次数是1000次,可以在命令行中动态调整cte_max_recursion_depth的值设置最大递归次数,比如我执行了命令

SET SESSION cte_max_recursion_depth = 10

再次执行程序,报错提示就变成了:

错误代码: 3636
Recursive query aborted after 11 iterations. Try increasing @@cte_max_recursion_depth to a larger value.

另外,还可以设置 max_execution_time 值限制递归查询最大超时时间,单位是毫秒,默认值0表示无超时时间限制。

3、使用limit终止递归

在mysql8.0.19开始,mysql开始支持使用limit语句终止递归,比如在上面的查询中:

WITH RECURSIVE cte (n) AS
(SELECT 1UNION ALLSELECT n + 1 FROM cte
)
SELECT * FROM cte

由于没有设置递归终止条件,会在递归1000次以后,触发系统递归最大次数的阈值上限从而报错。通过limit语句也可以终止递归:

WITH RECURSIVE cte (n) AS
(SELECT 1UNION ALLSELECT n + 1 FROM cte LIMIT 1001
)
SELECT * FROM cte

这样会查询出来1到1001共1001个数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/948917.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025 年进口地磅,出口地磅,100 吨地磅,120 吨地磅厂家最新推荐,产能、专利、环保三维数据透视!

引言 随着全球地磅市场年复合增长率达到 4.2%,100 吨、120 吨等中大型地磅需求持续攀升,进口与出口贸易规模同步扩大,但市场品牌良莠不齐的问题仍未解决。为精准甄选优质厂家,本次测评联合行业权威机构,基于三大核…

精通CTS与低功耗时钟设计

1、CTS在数字化时代的重要性 在数字化时代,IC(集成电路)设计日趋复杂,性能与功耗成为设计中的核心挑战。时钟树合成(CTS,Clock Tree Synthesis)作为物理实现流程中的关键环节,其目标是建立一个时钟分布网络,让…

GISDataMgr(数据管理工具)

瓦片导出工具,按需可以对数据包进行按照区域导出默认按省导出数据:也可以自定义导出区域:

202510月年口碑好的板式家具品牌前十榜单推荐

文章摘要 板式家具行业在2025年持续发展,注重环保、定制化和智能化趋势。本文基于市场调研和用户反馈,整理了板式家具品牌排名前十的列表,供消费者参考。排名综合考虑了品牌实力、产品质量、服务口碑等因素,旨在帮…

2025年板式家具品牌行业趋势与top5排名解析

摘要 板式家具行业在2025年预计将持续增长,受环保政策和消费升级驱动,市场规模有望突破千亿元。消费者对定制化、环保性能的需求日益增强,推动品牌创新。本文基于行业数据和用户评价,整理出top5推荐品牌表单,供采…

2025年10月口碑好的板式家具厂家前十名推荐

文章摘要 板式家具行业在2025年继续蓬勃发展,受益于环保材料创新和个性化定制趋势,市场规模预计年增长率达8-10%。消费者对家具的装饰性、耐用性和环保性要求日益提高,推动厂家不断提升产品质量和服务水平。本排名基…

学习笔记510—怎么去除”想要访问你的钥匙串中的密钥“Adobe Licensing ”若要给予许可

怎么去除”想要访问你的钥匙串中的密钥“Adobe Licensing ”若要给予许可新安装完PS,PR等Adobe软件,打开显示要输入密码,是什么密码??? 钥匙串密码是为了Mac系统为了保障电脑的安全设置的密码,简单的说就…

蓝狐家庭维修小程序系统:一站式家庭维修服务解决方案

一、概述总结 蓝狐家庭维修小程序系统是由小狐狸科技打造的一款专注于家庭维修服务的数字化解决方案,涵盖微信小程序与抖音小程序定制开发服务,同时提供最新版源码下载。该系统以 “便捷高效、功能全面” 为核心,构…

打造智慧体育场馆的“视觉中枢”:国标GB28181算法算力平台EasyGBS助力体育中心实现全域感知与智能升级

打造智慧体育场馆的“视觉中枢”:国标GB28181算法算力平台EasyGBS助力体育中心实现全域感知与智能升级一、方案背景 随着体育事业发展,体育场馆使用愈发频繁、活动规模扩大,安全保障、秩序维护和运营管理压力剧增。…

完整教程:【强化学习】#8 DQN(深度Q学习)

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

达梦删除数据文件后恢复

数据库没有关闭 [root@dmc1 arc]# rm -rf /dm/data/dmtj/tbs02.DBF[root@dmc1 arc]# ps -ef|grep dbsroot 248241 45882 0 11:47 pts/3 00:00:00 grep --color=auto dbs[root@dmc1 arc]# ps -ef|grep dmsdm…

贪心训练

AT_agc004_d [AGC004D] Teleporter 每个点只连一条边,

漫格搭子交友系统:一站式同城社交解决方案

一、概述总结 漫格搭子交友系统是一款专注于同城社交的多端解决方案,支持微信公众号、微信小程序等多种载体,以 “搭子交友” 为核心,整合了活动组织、群组互动、兴趣社交等多元功能,为用户打造便捷的同城交友与陪…

多功能名片小程序系统:助力企业与个人高效拓展人脉

一、概述总结 这款名片小程序系统是打造的高效人脉拓展工具,支持微信小程序和字节跳动小程序双平台适配,通过微擎系统在线交付,为用户提供全方位的电子名片解决方案。系统采用源码加密方式保障商品正品性。该系统集…

ETL调度最佳实践:避免高峰期任务冲突与资源争抢 - 指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

多线程基础-创建线程

线程的创建 线程的创建有三种方法继承Thread类,并重写run方法 实现Runable接口,并实现run方法 实现Callabke接口,并实现call方法(此处不介绍)案例:模拟文件下载 方法一: public class ThreadTest1 extends Thread {…

dataframe 和 numpy 数组有什么不同?

在cropformer相关的基因组预测任务场景中,DataFrame(通常指Pandas DataFrame)与NumPy数组均为数据处理核心格式,但二者在数据结构、功能定位、适用场景上存在显著差异,具体区别可结合文献中Cropformer的数据流处理…

《植物大战僵尸:重植版》无障碍补丁 | An accessibility mod for Plants vs. Zombies™: Replanted

开发中... 项目信息游戏版本:[Steam] 植物大战僵尸™:重植版 开发状态:开发中 项目源码:https://github.com/game-a11y/PvZ-Replanted-A11y 发布地址:(开发中,尚无可用发布版。想尝鲜可以自行导入源码使用) 项目…

rac日常维护

1.启动数据库oracle账号执行[oracle@rac01 ~]$srvctl start instance -d slnngk -i slnngk2[oracle@rac01 ~]$srvctl start database -d slnngk2.查看资源 grid账号下执行[grid@rac01 ~]$ crsctl stat res -t --------…

2025年上海直连全球云网络公司权威推荐榜单:AIGPU专用算力/GPU计费模式/GPU弹性算力源头厂家精选

随着全球数字化转型加速,直连云网络市场迎来爆发式增长,上海作为中国云计算产业的重要枢纽,其直连全球云网络服务能力备受关注。 据全球云计算市场分析报告显示,2024年全球直连云网络服务市场规模达到185亿美元,预…