MySQL 8 通用表表达式 with as
通用表表达式简称为CTE(Common Table Expressions)。CTE是命名的临时结果集,作用范围是当前语句。CTE可以理解成一个可以复用的子查询,当然跟子查询还是有点区别的,CTE可以引用其他CTE,但子查询不能引用其他子查询。
CTE的语法格式如下:
使用WITH语句创建CTE的情况如下:
(1)SELECT、UPDATE、DELETE语句的开头:
(2)在子查询的开头:
(3)紧接SELECT,在包含SELECT声明的语句之前:
下面通过案例来讲述通用表表达式的使用方法。
创建商品表goods,该数据表包含上下级关系的数据,具体字段包含商品编号(id)、商品名称(name)、上级商品的编号(gid)。创建语句如下:
CREATE TABLE `goods` ( `id` int(11) NOT NULL, `name` varchar(30) DEFAULT NULL, `gid` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; INSERT INTO `boot`.`goods` (`id`, `name`, `gid`) VALUES ('1', '商品', '0'); INSERT INTO `boot`.`goods` (`id`, `name`, `gid`) VALUES ('2', '水果', '1'); INSERT INTO `boot`.`goods` (`id`, `name`, `gid`) VALUES ('3', '蔬菜', '1'); INSERT INTO `boot`.`goods` (`id`, `name`, `gid`) VALUES ('4', '苹果', '2'); INSERT INTO `boot`.`goods` (`id`, `name`, `gid`) VALUES ('5', '香蕉', '2'); INSERT INTO `boot`.`goods` (`id`, `name`, `gid`) VALUES ('6', '菠菜', '3'); INSERT INTO `boot`.`goods` (`id`, `name`, `gid`) VALUES ('7', '萝卜', '3');
下面开始查询每个商品对应的上级商品名称。这里使用子查询的方式:
SELECT g.*,(select name from goods where id = g.gid) as pname FROM goods g;
接着使用CTE的方式,完成上述功能:
with cte as ( select * from goods ) SELECT g.*,(select name from cte where id = g.gid) as pname FROM goods g;
从结果可以看出,CTE是一个可以重复使用的结果集。相比于子查询,CTE的效率会更高,因为非递归的CTE只会查询一次并可以重复使用。
CTE可以引用其他CTE的结果。例如,下面的语句中,cte2就引用了cte1中的结果。
with cte1 as ( select * from goods ), cte2 as ( SELECT g.*,cte1.name as pname FROM goods g LEFT JOIN cte1 on g.gid = cte1.id ) select * from cte2;
还有一种特殊的CTE,就是递归CTE,其子查询会引用自身。WITH子句必须以WITH RECURSIVE开头。
CTE递归子查询包括两部分:seed查询和recursive查询,中间由union [all]或union distinct分隔。seed查询会被执行一次,以创建初始数据子集。recursive查询会被重复执行以返回数据子集,直到获得完整结果集。当迭代不会生成任何新行时,递归会停止。可以参看下面的案例:
with recursive cte(n) as ( select 1 union ALL select n+1 from cte where n < 8 ) select * from cte;
上面的语句会递归显示8行,每行分别显示1~8数字。递归的过程如下:
(1)首先执行SELECT 1得到结果1,即当前n的值为1。
(2)接着执行SELECT N+1 FROM cte WHERE n < 8,因为当前n为1,所以WHERE条件成立,生成新行,SELECT n+1得到结果2,即当前n的值为2。
(3)继续执行SELECT n+1 FROM cte WHERE n < 8,因为当前n为2,所以WHERE条件成立,生成新行,SELECT n+1得到结果3,即当前n的值为3。
(4)一直递归下去。
(5)直到当n为8时,where条件不成立,无法生成新行,递归停止。
下面使用递归CTE来查询每个商品到顶级商品的层次。
with recursive cte as ( select id,name,cast('0' as char(255)) as path from goods where gid = 0 union all select g.id,g.name,CONCAT(cte.path,',',cte.id) from goods g join cte on g.gid = cte.id ) select * from cte;
查询一个指定商品的所有父级商品。
with recursive cte as ( select id,name,gid from goods where id = 7 union all select g.id,g.name,g.gid from goods g join cte on cte.gid = g.id ) select * from cte;
文章来源:MySQL8从入门到精通-7.10
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
2017-03-25 css 蒙层
2017-03-25 css 多行文本的溢出显示省略号(移动端)
2017-03-25 移动端利用-webkit-box水平垂直居中(旧弹性盒)
2017-03-25 去除inline-block元素间间距
2017-03-25 js定时器setInterval()与setTimeout()