mysql8 公用表表达式CTE的使用方法实例分析

所属分类: 数据库 / Mysql 阅读数: 896
收藏 0 赞 0 分享

本文实例讲述了mysql8 公用表表达式CTE的使用方法。分享给大家供大家参考,具体如下:

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

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

一、cte的语法格式:

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

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

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

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

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

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

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

INSERT ... WITH ... SELECT ...
REPLACE ... WITH ... SELECT ...
CREATE TABLE ... WITH ... SELECT ...
CREATE VIEW ... WITH ... SELECT ...
DECLARE CURSOR ... WITH ... SELECT ...
EXPLAIN ... WITH ... SELECT ...

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

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

插入点数据:

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

四、非递归CTE

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

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

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

with cte as (
 select * from menu
) 
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 中的结果。

with cte1 as (
 select * from menu
), cte2 as (
 select m.*, cte1.name as pname from menu as m left join cte1 on m.pid = cte1.id 
)
select * from cte2;

 五、递归CTE

递归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 < 10
)
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就可以很好的处理了。

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

with recursive cte as (
 select id, name, cast('0' as char(255)) as path from menu where pid = 0
 union all
 select menu.id, menu.name, concat(cte.path, ',', cte.id) as path from menu inner join cte on menu.pid = cte.id
)
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、直到没有返回任何行时,递归停止。

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

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

更多关于MySQL相关内容感兴趣的读者可查看本站专题:《MySQL查询技巧大全》、《MySQL常用函数大汇总》、《MySQL日志操作技巧大全》、《MySQL事务操作技巧汇总》、《MySQL存储过程技巧大全》及《MySQL数据库锁相关技巧汇总

希望本文所述对大家MySQL数据库计有所帮助。

更多精彩内容其他人还在看

MariaDB(MySQL)创建、删除、选择及数据类型使用详解

这篇文章主要介绍了MariaDB(MySQL)创建、删除、选择及数据类型使用详解的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享

mysql索引学习教程

在mysql 中,索引可以分为两种类型 hash索引和 btree索引。这篇文章主要介绍了mysql索引的相关知识,非常不错,具有参考借鉴价值,感兴趣的朋友一起看看吧
收藏 0 赞 0 分享

MySQL修改默认字符集编码的方法

这篇文章主要介绍了MySQL修改默认字符集编码的方法的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

MySQL中实现插入或更新操作(类似Oracle的merge语句)

这篇文章主要介绍了在MySQL中实现插入或更新操作(类似Oracle的merge语句)的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

mysql5.x升级到mysql5.7后导入之前数据库date出错的快速解决方法

这篇文章主要介绍了mysql5.x升级到mysql5.7后导入之前数据库date出错的快速解决方法,非常不错,具有参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

LNMP下使用命令行导出导入MySQL数据库的方法

这篇文章主要介绍了LNMP下使用命令行导出导入MySQL数据库的方法,需要的朋友可以参考下
收藏 0 赞 0 分享

MySql中的IFNULL、NULLIF和ISNULL用法详解

在做项目中发现MySql里的isnull和mssql里的有点不同。接下来小编通过本文给大家介绍MySql中的IFNULL、NULLIF和ISNULL用法详解的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

Mysql 5.7 服务下载安装图文教程(经典版)

MySQL 5.7在诸多方面都进行了大幅的改进,主要在于安全性、灵活性、易用性、可用性和性能等几个方面。这篇文章主要介绍了Mysql5.7服务下载安装图文教程(经典版),需要的朋友可以参考下
收藏 0 赞 0 分享

SQL重复记录查询 查询多个字段、多表查询、删除重复记录的方法

下面小编就为大家带来一篇SQL重复记录查询 查询多个字段、多表查询、删除重复记录的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

mysql socket文件作用详解

这篇文章主要介绍了mysql socket文件作用的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享
查看更多