公用表表达式CTE就是命名的临时结果集,作用范围是当前语句。

说白点你可以理解成一个可以复用的子查询,当然跟子查询还是有点区别的,CTE可以引用其他CTE,但子查询不能引用其他子查询。

一、cte的语法格式:

  1. with_clause:
  2. WITH [RECURSIVE]
  3. cte_name [(col_name [, col_name] ...)] AS (subquery)
  4. [, cte_name [(col_name [, col_name] ...)] AS (subquery)] ...

二、哪些地方可以使用with语句创建cte

1、select, update,delete 语句的开头

  1. WITH ... SELECT ...
  2. WITH ... UPDATE ...
  3. WITH ... DELETE ...

2、在子查询的开头或派生表子查询的开头

  1. SELECT ... WHERE id IN (WITH ... SELECT ...) ...
  2. SELECT * FROM (WITH ... SELECT ...) AS dt ...

3、紧接SELECT,在包含 SELECT声明的语句之前

  1. INSERT ... WITH ... SELECT ...
  2. REPLACE ... WITH ... SELECT ...
  3. CREATE TABLE ... WITH ... SELECT ...
  4. CREATE VIEW ... WITH ... SELECT ...
  5. DECLARE CURSOR ... WITH ... SELECT ...
  6. EXPLAIN ... WITH ... SELECT ...

  

三、我们先建个表,准备点数据

  1. CREATE TABLE `menu` (
  2. `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
  3. `name` varchar(32) DEFAULT '' COMMENT '名称',
  4. `url` varchar(255) DEFAULT '' COMMENT 'url地址',
  5. `pid` int(11) DEFAULT '0' COMMENT '父级ID',
  6. PRIMARY KEY (`id`)
  7. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

插入点数据:

  1. INSERT INTO `menu` (`id`, `name`, `url`, `pid`) VALUES ('1', '后台管理', '/manage', '0');
  2. INSERT INTO `menu` (`id`, `name`, `url`, `pid`) VALUES ('2', '用户管理', '/manage/user', '1');
  3. INSERT INTO `menu` (`id`, `name`, `url`, `pid`) VALUES ('3', '文章管理', '/manage/article', '1');
  4. INSERT INTO `menu` (`id`, `name`, `url`, `pid`) VALUES ('4', '添加用户', '/manage/user/add', '2');
  5. INSERT INTO `menu` (`id`, `name`, `url`, `pid`) VALUES ('5', '用户列表', '/manage/user/list', '2');
  6. INSERT INTO `menu` (`id`, `name`, `url`, `pid`) VALUES ('6', '添加文章', '/manage/article/add', '3');
  7. INSERT INTO `menu` (`id`, `name`, `url`, `pid`) VALUES ('7', '文章列表', '/manage/article/list', '3');

四、非递归CTE

这里查询每个菜单对应的直接上级名称,通过子查询的方式。

  1. select m.*, (select name from menu where id = m.pid) as pname from menu as m;

这里换成用cte完成上面的功能

  1. with cte as (
  2. select * from menu
  3. )
  4. select m.*, (select cte.name from cte where cte.id = m.pid) as pname from menu as m;

上面的示例并不是很好,只是用来演示cte的使用。你只需要知道 cte 就是一个可复用的结果集就好了。

相比较某些子查询,cte 的效率会更高,因为非递归的 cte 只会查询一次并复用。

cte 可以引用其他 cte 的结果,比如下面的语句,cte2 就引用了 cte1 中的结果。

  1. with cte1 as (
  2. select * from menu
  3. ), cte2 as (
  4. select m.*, cte1.name as pname from menu as m left join cte1 on m.pid = cte1.id
  5. )
  6. select * from cte2;

  

五、递归CTE

递归cte是一种特殊的cte,其子查询会引用自身,with子句必须以 with recursive 开头。

cte递归子查询包括两部分:seed 查询 和 recursive 查询,中间由union [all] 或 union distinct 分隔。

seed 查询会被执行一次,以创建初始数据子集。

recursive 查询会被重复执行以返回数据子集,直到获得完整结果集。当迭代不会生成任何新行时,递归会停止。

  1. with recursive cte(n) as (
  2. select 1
  3. union all
  4. select n + 1 from cte where n < 10
  5. )
  6. select * from cte;

上面的语句,会递归显示10行,每行分别显示1-10数字。

递归的过程如下:

1、首先执行 select 1 得到结果 1, 则当前 n 的值为 1。

2、接着执行 select n + 1 from cte where n < 10,因为当前 n 为 1,所以where条件成立,生成新行,select n + 1 得到结果 2,则当前 n 的值为 2。

3、继续执行 select n + 1 from cte where n < 10,因为当前 n 为 2,所以where条件成立,生成新行,select n + 1 得到结果 3,则当前 n 的值为 3。

4、一直递归下去

5、直到当 n 为 10 时,where条件不成立,无法生成新行,则递归停止。

对于一些有上下级关系的数据,通过递归cte就可以很好的处理了。

比如我们要查询每个菜单到顶级菜单的路径

  1. with recursive cte as (
  2. select id, name, cast('0' as char(255)) as path from menu where pid = 0
  3. union all
  4. select menu.id, menu.name, concat(cte.path, ',', cte.id) as path from menu inner join cte on menu.pid = cte.id
  5. )
  6. select * from cte;

递归的过程如下:

1、首先查询出所有 pid = 0 的菜单数据,并设置path 为 '0',此时cte的结果集为 pid = 0 的所有菜单数据。

2、执行 menu inner join cte on menu.pid = cte.id ,这时表 menu 与 cte (步骤1中获取的结果集) 进行内连接,获取菜单父级为顶级菜单的数据。

3、继续执行 menu inner join cte on menu.pid = cte.id,这时表 menu 与 cte (步骤2中获取的结果集) 进行内连接,获取菜单父级的父级为顶级菜单的数据。

4、一直递归下去

5、直到没有返回任何行时,递归停止。

查询一个指定菜单所有的父级菜单

  1. with recursive cte as (
  2. select id, name, pid from menu where id = 7
  3. union all
  4. select menu.id, menu.name, menu.pid from menu inner join cte on cte.pid = menu.id
  5. )
  6. select * from cte;

mysql8 公用表表达式CTE的使用的更多相关文章

  1. SQL Server中公用表表达式 CTE 递归的生成帮助数据,以及递归的典型应用

    本文出处:http://www.cnblogs.com/wy123/p/5960825.html 我们在做开发的时候,有时候会需要一些帮助数据,必须需要连续的数字,连续间隔的时间点,连续的季度日期等等 ...

  2. 公用表表达式CTE

    公用表表达式CTE表面上和派生表非常相似,看起来只是语义上的区别.但和派生表比较起来,CTE具有几个优势:第一,如果须要在一个CTE中引用另一个CTE,不需要像派生表那样嵌套,相反,只要简单地在同一个 ...

  3. T-SQL 公用表表达式(CTE)

    公用表表达式(CTE) 在编写T-SQL代码时,往往需要临时存储某些结果集.前面我们已经广泛使用和介绍了两种临时存储结果集的方法:临时表和表变量.除此之外,还可以使用公用表表达式的方法.公用表表达式( ...

  4. 详解公用表表达式(CTE)

    简介 对于SELECT查询语句来说,通常情况下,为了使T-SQL代码更加简洁和可读,在一个查询中引用另外的结果集都是通过视图而不是子查询来进行分解的.但是,视图是作为系统对象存在数据库中,那对于结果集 ...

  5. T-SQL查询进阶--详解公用表表达式(CTE)

    简介 对于SELECT查询语句来说,通常情况下,为了使T-SQL代码更加简洁和可读,在一个查询中引用另外的结果集都是通过视图而不是子查询来进行分解的. 但是,视图是作为系统对象存在数据库中,那对于结果 ...

  6. 公用表表达式 (CTE)、递归、所有子节点、sqlserver

    指定临时命名的结果集,这些结果集称为公用表表达式 (CTE).公用表表达式可以包括对自身的引用.这种表达式称为递归公用表表达式. 对于递归公用表达式来说,实现原理也是相同的,同样需要在语句中定义两部分 ...

  7. SQL Server进阶(六)表表达式--派生表、公用表表达式(CTE)、视图和内联表值函数

    概述 表表达式是一种命名的查询表达式,代表一个有效地关系表.可以像其他表一样,在数据处理中使用表表达式. SQL Server支持四种类型的表表达式:派生表,公用表表达式,视图和内联表值函数. 为什么 ...

  8. 公用表表达式(CTE)

    在编写T-SQL代码时,往往需要临时存储某些结果集.前面我们已经广泛使用和介绍了两种临时存储结果集的方法:临时表和表变量.除此之外,还可以使用公用表表达式的方法.公用表表达式(Common Table ...

  9. SQL Server 公用表表达式(CTE)实现递归

    公用表表达式简介: 公用表表达式 (CTE) 可以认为是在单个 SELECT.INSERT.UPDATE.DELETE 或 CREATE VIEW 语句的执行范围内定义的临时结果集.CTE 与派生表类 ...

随机推荐

  1. LeetCode OJ 15. 3Sum

    题目 Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all ...

  2. Android进阶AIDL - 2018年4月14日

    参考:慕课网 --- 最后三集.Android开发艺术探索 1.在AS中创建aidl文件后,要编译一下才会在gen下生成debug文件: 2.AIDL 不支持short类型,常用的数据类型: 3.AI ...

  3. java第一课总结

    转眼间开学了,我们也正式进入了大二.心里既有激动,又有些感慨,还带有一些担忧.激动的是我们褪去了大一的稚气成为了一名大二的学长了,第一次体会到了大学学长的感觉,心里很是激动.感慨的是我们又成长了一岁, ...

  4. @Scope 注解

    @Scope(value=ConfigurableBeanFactory.SCOPE_PROTOTYPE)这个是说在每次注入的时候回自动创建一个新的bean实例 @Scope(value=Config ...

  5. Real Time Render 4

    [Real Time Render 4] 1.Radiometry(辐射测试) deals with the measurement of electromagnetic(电磁) radiation( ...

  6. app.route()

    [app.route()] 可使用 app.route() 创建路由路径的链式路由句柄.由于路径在一个地方指定,这样做有助于创建模块化的路由,而且减少了代码冗余和拼写错误.请参考 Router() 文 ...

  7. msf客户端渗透(四):关闭UAC,hash、防火墙、服务、磁盘、DEP、防病毒、远程桌面、桌面截图、获取tooken

    关闭UAC 如果你获得一个system权限的session 进入到这个session 进入到shell 依次输入以下命令 cmd.exe /k %windir%\System32\reg.exe AD ...

  8. python对ftp进行操作

    背景:需要对ftp进行操作,涉及上传和下载操作   from ftplib import FTP ftp = FTP(host=“ftp的hostname",user=‘登录用户名', pa ...

  9. https方式下 git push 每次都要输入密码的解决办法

    转载自:http://git.oschina.net/oschina/git-osc/issues/2586   作者:Zoker https方式每次都要输入密码,按照如下设置即可输入一次就不用再手输 ...

  10. SpringCloud报错:com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server

    启动SpringCloudEureka 报错:com.netflix.discovery.shared.transport.TransportException: Cannot execute req ...