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

本文实例讲述了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数据库计有所帮助。

(0)

相关推荐

  • Sql学习第二天——SQL DML与CTE概述

    DML (Data Manipulation Language) 与 CTE (Common Table Expression) 今天看书时遇到的两个缩写,不知道其含义,于是就百度了一下,特地在此记录下来,以便于下次复习使用. 关于DML (Data Manipulation Language): 数据操纵语言,用户能够查询数据库以及操作已有数据库中的数据的计算机语言.具体是指是UPDATE更新.INSERT插入.DELETE删除. 关于CTE (Common Table Expression)

  • 详解MySQL8.0+常用命令

    开启远程访问 通过以下命令开启root用户远程访问权限: CREATE USER 'root'@'%' IDENTIFIED BY 'password'; GRANT ALL ON *.* TO 'root'@'%'; ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'password'; FLUSH PRIVILEGES; 注:其中,password为root的密码,FLUSH PRIVILEGES为刷新权限 导入数

  • MySql8.0以上版本正确修改ROOT密码的方法

    部署环境: 安装版本red hat Cent 7.0 MYSQL 版本 8.0.2.0 成功部署完毕后出现故障情况: 1.      正常启动MYSQL服务后,敲Linux中root账户和密码进入不去. 2.      从/etc/my.cnf 配置文件中加入skip-grant-table后正常登陆,但是不能创建用户等多操作 总结来说: 想进去mysql后不能操作多指令,操作多指令又不能进去mysql,死循环 挖坑环节: 网上找了很多办法,首先加入skip-grant-table.后进去刷新权

  • 详解MySQL 8.0.18命令

    打开刚刚解压的文件夹 C:\web\mysql-8.0.11 ,在该文件夹下创建 my.ini 配置文件,编辑 my.ini 配置以下基本信息: [client] # 设置mysql客户端默认字符集 default-character-set=utf8 [mysqld] # 设置3306端口 port = 3306 # 设置mysql的安装目录 basedir=C:\\web\\mysql-8.0.11 # 设置 mysql数据库的数据的存放目录,MySQL 8+ 不需要以下配置,系统自己生成即

  • Sql学习第三天——SQL 关于CTE(公用表达式)的递归查询使用

    关于使用CTE(公用表表达式)的递归查询----SQL Server 2005及以上版本 公用表表达式 (CTE) 具有一个重要的优点,那就是能够引用其自身,从而创建递归 CTE.递归 CTE 是一个重复执行初始 CTE 以返回数据子集直到获取完整结果集的公用表表达式. 当某个查询引用递归 CTE 时,它即被称为递归查询.递归查询通常用于返回分层数据,例如:显示某个组织图中的雇员或物料清单方案(其中父级产品有一个或多个组件,而那些组件可能还有子组件,或者是其他父级产品的组件)中的数据. 递归 C

  • MySQL8.0 DDL原子性特性及实现原理

    1. DDL原子性概述 8.0之前并没有统一的数据字典dd,server层和引擎层各有一套元数据,sever层的元数据包括(.frm,.opt,.par,.trg等),用于存储表定义,分区表定义,触发器定义等信息:innodb层也有自己一套元数据,包括表信息,索引信息等,这两套元数据并没有机制保证一致性,这就导致了在异常情况下可能存在元数据不一致问题,一种典型场景下,删表操作,sever层的frm已经成功删除了,但引擎层数据字典并没有更新,导致再建重名表失败的问题.同样的,比如drop tabl

  • MySQL8新特性:自增主键的持久化详解

    前言 自增主键没有持久化是个比较早的bug,这点从其在官方bug网站的id号也可看出(https://bugs.mysql.com/bug.php?id=199).由Peter Zaitsev(现Percona CEO)于2003年提出.历史悠久且臭名昭著. 首先,直观的重现下. mysql> create table t1(id int auto_increment primary key); Query OK, 0 rows affected (0.01 sec) mysql> inser

  • MySQL 8.0.18给数据库添加用户和赋权问题

    1. 首选使用root用户登录数据库  mysql -uroot -p 2. 使用mysql 数据库 USE mysql; 3. 为mysql创建用户:dev 密码为:dev12345 CREATE USER dev IDENTIFIED BY 'dev12345'; 4. 查看下用户dev的权限 SELECT * FROM user WHERE user='dev' ; SHOW GRANTS FOR dev; 5. 给用户dev在数据库名为dev_db上赋EXECUTE(执行存储过程),IN

  • 关于SQL中CTE(公用表表达式)(Common Table Expression)的总结

    一.WITH AS的含义 WITH AS短语,也叫做子查询部分(subquery factoring),可以让你做很多事情,定义一个SQL片断,该SQL片断会被整个SQL语句所用到.有的时候,是为了让SQL语句的可读性更高些,也有可能是在UNION ALL的不同部分,作为提供数据的部分. 特别对于UNION ALL比较有用.因为UNION ALL的每个部分可能相同,但是如果每个部分都去执行一遍的话,则成本太高,所以可以使用WITH AS短语,则只要执行一遍即可.如果WITH AS短语所定义的表名

  • Mysql8.0使用窗口函数解决排序问题

    MySQL窗口函数简介 MySQL从8.0开始支持窗口函数,这个功能在大多商业数据库和部分开源数据库中早已支持,有的也叫分析函数. 什么叫窗口? 窗口的概念非常重要,它可以理解为记录集合,窗口函数也就是在满足某种条件的记录集合上执行的特殊函数.对于每条记录都要在此窗口内执行函数,有的函数随着记录不同,窗口大小都是固定的,这种属于静态窗口:有的函数则相反,不同的记录对应着不同的窗口,这种动态变化的窗口叫滑动窗口. 窗口函数和普通聚合函数也很容易混淆,二者区别如下: 聚合函数是将多条记录聚合为一条:

  • SqlServer使用公用表表达式(CTE)实现无限级树形构建

    SQL Server 2005开始,我们可以直接通过CTE来支持递归查询,CTE即公用表表达式 公用表表达式(CTE),是一个在查询中定义的临时命名结果集将在from子句中使用它.每个CTE仅被定义一次(但在其作用域内可以被引用任意次),并且在该查询生存期间将一直生存.可以使用CTE来执行递归操作. DECLARE @Level INT=3 ;WITH cte_parent(CategoryID,CategoryName,ParentCategoryID,Level) AS ( SELECT c

  • MySQL8.0新特性之支持原子DDL语句

    MySQL 8.0开始支持原子数据定义语言(DDL)语句.此功能称为原子DDL.原子DDL语句将与DDL操作关联的数据字典更新,存储引擎操作和二进制日志写入组合到单个原子事务中.即使服务器在操作期间暂停,也会提交事务,并将适用的更改保留到数据字典,存储引擎和二进制日志,或者回滚事务. 通过在MySQL 8.0中引入MySQL数据字典,可以实现Atomic DDL.在早期的MySQL版本中,元数据存储在元数据文件,非事务性表和存储引擎特定的字典中,这需要中间提交.MySQL数据字典提供的集中式事务

随机推荐