我的编程空间,编程开发者的网络收藏夹
学习永远不晚

深入理解MySQL公共表表达式

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

深入理解MySQL公共表表达式

公共表表达式(Common Table Expressions, CTE)是mysql在单一语句中执行过程中,预先定义的临时结果集。

有时我们需要在一个SQL中重复执行同一个子查询,而每次子查询都会重新计算结果,带来性能的浪费。而采用CTE可以在查询的一开始就定义好子查询的结果集,MySQL只会计算一次结果,然后在查询中使用CTE的名称可以反复引用。

一、CTE定义及分类

CTE的定义方式是在with子句后跟一个子查询,如果一个SQL中需要定义多个CTE,则用逗号分隔即可。

定义语法:

with_clause:
    WITH [RECURSIVE]
        cte_name [(col_name [, col_name] ...)] AS (subquery)
        [, cte_name [(col_name [, col_name] ...)] AS (subquery)] ...

CTE分为两种:

  • 普通CTE:定义一个简单子查询
  • 递归CTE:定义时可以引用自己,产生一个递归的结果集

普通CTE和递归CTE的区别在于,递归CTE多了一个recursive关键字,且需要引用自己。

二、普通CTE

2.1 普通CTE示例

以下的演示SQL可以在在MySQL的官方示例数据库中执行:

with
cte1 as (select emp_no,first_name,last_name from employees where emp_no=10012), -- 定义cte1
cte2 as (select emp_no,dept_no from dept_emp)    -- 定义cte2
select cte1.emp_no,cte2.dept_no,cte1.first_name
from cte1
join cte2 on cte1.emp_no=cte2.emp_no;

深入理解MySQL公共表表达式

示例中在select子句前定义了cte1和cte2(以逗号分隔),随后在select子句中可以直接引用cte1和cte2的名称进行查询。

cte定义时也可以引用其他cte,例如在上面的定义中,cte2的定义可以引用cte1:

with
cte1 as (select emp_no,first_name,last_name from employees where emp_no=10012),
cte2 as (select emp_no,last_name from cte1)    -- cte2的定义引用了cte1
select cte1.emp_no,cte2.last_name
from cte1
join cte2 on cte1.emp_no=cte2.emp_no;

深入理解MySQL公共表表达式

注意之只有后定义的cte可以引用前面的定义的cte,如果把cte2定义位置调到前面,则会报错:cte1不存在.

深入理解MySQL公共表表达式

cte定义的名称后面可以添加括号,显式定义cte的列名,但要和后面子查询返还的列数量相同:

with
cte1(col1, col2, col3) as (select emp_no,first_name,last_name from employees where emp_no=10012)
select col1, col2, col3    -- 引用定义的列名
from cte1;

深入理解MySQL公共表表达式

此时后续cte则必须通过显示定义的列名来引用(col1, col2, col3),定义中子查询的列名不能再引用了。

2.2 CTE的使用场景

cte的定义不仅仅用在select中,也可以用在update/delete语句前,子查询中,以及其他可以嵌套select语句的地方(例如 insert …select):

  • WITH ... SELECT ...
  • WITH ... UPDATE ...
  • WITH ... DELETE …
  • SELECT ... WHERE id IN (WITH ... SELECT ...) ...
  • SELECT * FROM (WITH ... SELECT ...) AS dt ...
  • INSERT ... WITH ... SELECT ...
  • REPLACE ... WITH ... SELECT ...
  • CREATE TABLE ... WITH ... SELECT ...
  • CREATE VIEW ... WITH ... SELECT ...
  • DECLARE CURSOR ... WITH ... SELECT ...
  • EXPLAIN ... WITH ... SELECT ...

三、递归CTE

3.1 递归CTE示例

如果一个cte定义过程中引用了自己,则是递归cte,此时需要with recursive子句定义,其中recursive关键字是必须的。

递归cte包含2个部分,使用union all 或 union [distinct]连接:

with recursive
cte(n) as (
select 1
union all
select n+1 from cte where n<5)
select * from cte;

深入理解MySQL公共表表达式

上述cte定义中第1部分生成了一条初始数据,union all后面的第二部分引用了cte自己,且递归执行,直到不再满足条件(n<5)。

1个递归cte其实包含了非递归部分和递归部分,递归的第二部分每次都以上一次产生的结果集为基础计算数据。但是大小是以非递归部分为准,如果递归产生列越来越长,可能会发生错误。

例如下面的递归拼接:

with recursive
cte as (
select 1 as n, 'abc' as str    -- 非递归部分
union all
select n+1,concat(str,str) from cte where n<3)    -- 递归部分
select * from cte;

深入理解MySQL公共表表达式

如上图所示,在strict SQL模式下,因为第二列以非递归部分的长度为准,递归后长度列的长度变长导致SQL直接报错。

而在非strict SQL模式下,以上SQL可以执行成功,但是第二列都被按非递归部分截断了,如下所示:

深入理解MySQL公共表表达式

在遇到此类cte定义时,将非递归部分的列定义大一些,例如下面将'abc'的非递归部分加长,即可显示正确的递归结果:

with recursive
cte as (
select 1 as n, cast('abc' as char(20)) as str    -- 定义长度
union all
select n+1,concat(str,str) from cte where n<3)
select * from cte;

深入理解MySQL公共表表达式

另外,对于递归cte的递归部分(即union后的SQL)还有部分使用限制:

  • 递归部分不能包含聚合函数、窗口函数、group by、order by、distinct
  • 递归部分引用自身只能引用一次且必须在from子句中,不能在子查询中。

3.2 限制无限递归

对于递归cte,如果没有加限制递归的条件,在逻辑上是可以无限递归的(死循环)。为了限制这种情况,MySQL有4种解决方式:

  • 使用参数cte_max_recursion_depth来限制最大递归的次数,超过递归深度强制终止。
  • 使用参数max_execution_time来限制最大的执行时间。
  • 使用优化器提示 MAX_EXECUTION_TIME来限制最大执行时间。
  • MySQL 8.0.19后,可以用limit子句限制最大返还行数。

示例:通过cte_max_recursion_depth限制递归次数,超过10次递归终止

set session cte_max_recursion_depth=10;  -- 全局默认值是1000,我们这里修改会话级为10次
with recursive
cte(n) as (
select 1
union all
select n+1 from cte)
select * from cte;

深入理解MySQL公共表表达式

示例:超过10毫秒终止递归

set session cte_max_recursion_depth=100000;   -- 将递归次数增大,防止先触发
set session max_execution_time=10;    -- 将最大递归执行时长修改为10毫秒
with recursive
cte(n) as (
select 1
union all
select n+1 from cte)
select * from cte;

深入理解MySQL公共表表达式

示例:使用优化器提示限制递归执行时间

with recursive
cte(n) as (
select 1
union all
select n+1 from cte)
select  * from cte;    -- 使用提示语法限制执行时间

深入理解MySQL公共表表达式

四、一个递归CTE应用示例

假设我们有一张订单表,

create table orders (dt date,price decimal(10,2));
insert into orders values
('2022-01-01',100),
('2022-01-01',200),
('2022-01-03',200),
('2022-01-03',200),
('2022-01-05',300),
('2022-01-07',200);

现在要统计截止'2022-01-07'日的营业额,正常我们使用group by按日期汇集订单金额即可:

select dt, sum(price) sales from orders group by dt;

深入理解MySQL公共表表达式

但是注意到由于2号/4号/6号没有订单,所以查询出来的结果中不包含这些日期,而通过递归cte我们可以先按日期递归,将这些日期列出来然后与orders连接:

with recursive cte(dt) as (
select min(dt) from orders
union all
select dt + interval 1 day from cte where dt <(select max(dt) from orders))
select e.dt,ifnull(sum(o.price),0) turnover
from cte e
left join orders o on o.dt=e.dt
group by e.dt
order by e.dt;

深入理解MySQL公共表表达式

可以看到没有订单的日期也显示出来了,营业额显示为0,这个技巧在做报表类数据时很有用。

到此这篇关于深入理解MySQL公共表表达式的文章就介绍到这了,更多相关MySQL公共表表达式内容请搜索编程网(www.lsjlt.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.lsjlt.com)!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

深入理解MySQL公共表表达式

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

深入理解MySQL公共表表达式

目录一、CTE定义及分类二、普通CTE2.1 普通CTE示例2.2 CTE的使用场景三、递归CTE3.1 递归CTE示例3.2 限制无限递归四、一个递归CTE应用示例公共表表达式(Common Table Expressions, CTE)
深入理解MySQL公共表表达式
2024-09-04

MySQL 递归 CTE(公用表表达式)

MySQL 中的递归 CTE(公用表表达式)是一种处理递归查询的方法。CTE 允许您在查询中创建临时表,并在查询中引用该临时表。以递归查询为例,假设有一个员工表,其中包含员工的 ID、姓名和经理的 ID。现在需要找出某个员工的所有下属(直接
2023-10-20

深入理解Lambda表达式与函数式接口

Java8被称作Java史上变化最大的一个版本。其中包含很多重要的新特性,最核心的就是增加了Lambda表达式和Stream API。这两者也可以结合在一起使用。【推荐学习:java视频教程】首先来看下什么是Lambda表达式。Lambda表达式,维基百科上的
深入理解Lambda表达式与函数式接口
2016-02-15

MariaDB表中的公用表表达式CTE怎么理解

今天就跟大家聊聊有关MariaDB表中的公用表表达式CTE怎么理解,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。前言公用表表达式(Common Table Expression,CT
2023-06-29

MyBatis ORM的动态SQL表达式深入解析

MyBatis 是一个优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和
MyBatis ORM的动态SQL表达式深入解析
2024-09-11

深入浅析Java8中的lambda表达式

深入浅析Java8中的lambda表达式?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。函数式接口函数式接口(functional interface 也叫功能性接口,其实是同一
2023-05-31

深入浅出解析正则表达式-替换原则

正则表达式的替换原则是指在使用正则表达式进行替换操作时,根据指定的正则表达式模式匹配到的内容将被替换为指定的替换字符串。在正则表达式中,可以使用一些特殊字符和语法来描述要匹配的模式。常用的替换原则包括:1. 字面替换:将匹配到的字符串直接替
2023-08-11

理解python正则表达式

在python中,对正则表达式的支持是通过re模块来支持的。使用re的步骤是先把表达式字符串编译成pattern实例,然后在使用pattern去匹配文本获取结果。 其实也有另外一种方式,就是直接使用re模块的方法,但是这样就不能使用编译后的
2022-06-04

Java8深入学习系列(一)lambda表达式介绍

前言最近在学习java8,所以接下来会给大家介绍一系列的Java8学习内容,那么让我们先从lambda表达式开始。众所周知从java8出现以来lambda是最重要的特性之一,它可以让我们用简洁流畅的代码完成一个功能。 很长一段时间java被
2023-05-31

深入浅析正则表达式在Java中的作用

这篇文章将为大家详细讲解有关深入浅析正则表达式在Java中的作用,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。正则表达式是什么?用我的理解就是一个表达式。用来匹配,替换,判断字符串,之前业务
2023-05-31

编程热搜

目录