公用表达式(Common Table Expressions,简称CTE)

Maria DB 版本为10.2.2以上的才支持 WITH 语法

CTE 介绍

WITH关键字表示公用表表达式(CTE)

它使您可以在查询中多次引用子查询表达式,就好像有一个仅在查询期间存在的临时表一样。

语法(Syntax)

  1. WITH [RECURSIVE] table_reference as (SELECT ...)
  2. SELECT ...

您可以将table_reference用作外部SELECT部分中的任何普通表。您也可以在子查询中使用WITH。WITH也可以与EXPLAINSELECT一起使用。

以下是在顶级使用WITH的示例:

  1. WITH t AS (SELECT a FROM t1 WHERE b >= 'c')
  2. SELECT * FROM t2, t WHERE t2.c = t.a;

下面的示例在子查询中使用WITH:

  1. SELECT t1.a, t1.b FROM t1, t2
  2. WHERE t1.a > t2.c
  3. AND t2.c IN(WITH t AS (SELECT * FROM t1 WHERE t1.a < 5)
  4. SELECT t2.c FROM t2, t WHERE t2.c = t.a);

以下是递归CTE的示例:

  1. WITH RECURSIVE ancestors AS
  2. ( SELECT * FROM folks
  3. WHERE name="Alex"
  4. UNION
  5. SELECT f.*
  6. FROM folks AS f, ancestors AS a
  7. WHERE f.id = a.father OR f.id = a.mother )
  8. SELECT * FROM ancestors;

公用表表达式(CTE)是标准的SQL功能,本质上是临时命名的结果集

CTE有两种类别:

  • 非递归CTE
  • 递归CTE

非递归CTE

WITH关键字表示CTE,它具有一个名称,后跟一个正文(主要查询的语句),如下所示

CTE与派生表相似。例如:

  1. WITH engineers AS
  2. ( SELECT * FROM employees
  3. WHERE dept = 'Engineering' )
  4. SELECT * FROM engineers
  5. WHERE ...

顶级使用CTE

  1. SELECT * FROM
  2. ( SELECT * FROM employees
  3. WHERE dept = 'Engineering' ) AS engineers
  4. WHERE ...

子查询中使用CTE

基本上,非递归CTE是本地查询的VIEW。有许多优点和警告。该语法比嵌套的FROM(SELECT ...)更具可读性。一个CTE可以引用另一个CTE,并且可以从多个地方引用它。

引用另一个CTE的CTE

与嵌套的FROM(SELECT ...)子句相比,使用这种格式可使SQL更具可读性。下面是一个示例:

  1. WITH engineers AS (
  2. SELECT * FROM employees
  3. WHERE dept IN('Development','Support') ),
  4. eu_engineers AS ( SELECT * FROM engineers WHERE country IN('NL',...) )
  5. SELECT
  6. ...
  7. FROM eu_engineers;

CTE的多种用途

这可以是“anti-self join”,例如:

  1. WITH engineers AS (
  2. SELECT * FROM employees
  3. WHERE dept IN('Development','Support') )
  4. SELECT * FROM engineers E1
  5. WHERE NOT EXISTS
  6. (SELECT 1 FROM engineers E2
  7. WHERE E2.country=E1.country
  8. AND E2.name <> E1.name );

或者,对于逐年比较,例如:

  1. WITH sales_product_year AS (
  2. SELECT product, YEAR(ship_date) AS year,
  3. SUM(price) AS total_amt
  4. FROM item_sales
  5. GROUP BY product, year )
  6. SELECT *
  7. FROM sales_product_year CUR,
  8. sales_product_year PREV,
  9. WHERE CUR.product=PREV.product
  10. AND CUR.year=PREV.year + 1
  11. AND CUR.total_amt > PREV.total_amt

另一个用途是将个人与他们的团体进行比较。以下是如何执行此操作的示例:

  1. WITH sales_product_year AS (
  2. SELECT product,
  3. YEAR(ship_date) AS year,
  4. SUM(price) AS total_amt
  5. FROM item_sales
  6. GROUP BY product, year
  7. )
  8. SELECT *
  9. FROM sales_product_year S1
  10. WHERE
  11. total_amt >
  12. (SELECT 0.1 * SUM(total_amt)
  13. FROM sales_product_year S2
  14. WHERE S2.year = S1.year)

递归CTE表达式

公用表表达式(CTE)是标准的SQL功能,本质上是临时命名的结果集。CTE最初于1999年出现在SQL标准中,而第一个实现则于2007年开始出现。

SQL通常在递归结构方面很差。

CTE允许查询引用自身。递归CTE将重复执行数据的子集,直到获得完整的结果集。这对于处理分层或树状数据特别有用。max_recursive_iterations 避免了无限循环。

语法示例

WITH RECURSIVE 表示递归CTE,它具有一个名称,后跟一个正文(主要查询),如下所示:

计算方式(Computation)

给出以下结构:

首先执行查询的锚点部分:

接下来,执行查询的递归部分:

Summary so far(到目前为止的总结)

  1. with recursive R as (
  2. select anchor_data
  3. union [all]
  4. select recursive_part
  5. from R, ...
  6. )
  7. select ...
  1. 计算anchor_data
  2. 计算recursive_part以获取新数据
  3. 如果(新数据为非空)转到2;

CAST避免截断数据

正如MariaDB和SQL标准当前实现的那样,如果数据转换不正确,数据可能会被截断。如果CTE的递归部分为列生成的值比CTE的非递归部分宽,则必须将列CAST正确的宽度。

Examples

传递闭包-确定总线目的地

样本数据:

  1. CREATE TABLE bus_routes (origin varchar(50), dst varchar(50));
  2. INSERT INTO bus_routes VALUES
  3. ('New York', 'Boston'),
  4. ('Boston', 'New York'),
  5. ('New York', 'Washington'),
  6. ('Washington', 'Boston'),
  7. ('Washington', 'Raleigh');

现在,我们要返回以纽约(New York)为起点的巴士目的地:

  1. WITH RECURSIVE bus_dst as (
  2. SELECT origin as dst FROM bus_routes WHERE origin='New York'
  3. UNION
  4. SELECT bus_routes.dst FROM bus_routes, bus_dst WHERE bus_dst.dst= bus_routes.origin
  5. )
  6. SELECT * FROM bus_dst;
  7. +------------+
  8. | dst |
  9. +------------+
  10. | New York |
  11. | Boston |
  12. | Washington |
  13. | Raleigh |
  14. +------------+

上面的示例计算如下:

首先,计算anchor 数据:

  • 从纽约(New York)出发
  • 添加了波士顿(Boston)和华盛顿(Washington)

接下来,递归部分:

  • 从波士顿(Boston)出发,然后从华盛顿(Washington)出发
  • 罗利(Raleigh)被添加
  • UNION排除已经存在的节点。
计算路径-确定总线(Bus)路线

这次,我们尝试获取诸如“纽约->华盛顿->罗利”之类的巴士路线。

New York -> Washington -> Raleigh

使用与上一个示例相同的样本数据:

  1. WITH RECURSIVE paths (cur_path, cur_dest) AS (
  2. SELECT origin, origin FROM bus_routes WHERE origin='New York'
  3. UNION
  4. SELECT CONCAT(paths.cur_path, ',', bus_routes.dst), bus_routes.dst
  5. FROM paths, bus_routes
  6. WHERE paths.cur_dest = bus_routes.origin AND
  7. NOT FIND_IN_SET(bus_routes.dst, paths.cur_path)
  8. )
  9. SELECT * FROM paths;
  10. +-----------------------------+------------+
  11. | cur_path | cur_dest |
  12. +-----------------------------+------------+
  13. | New York | New York |
  14. | New York,Boston | Boston |
  15. | New York,Washington | Washington |
  16. | New York,Washington,Boston | Boston |
  17. | New York,Washington,Raleigh | Raleigh |
  18. +-----------------------------+------------+
CAST避免数据被截断

在下面的示例中,数据被截断,因为结果没有明确地转换为足够宽的类型:

  1. WITH RECURSIVE tbl AS (
  2. SELECT NULL AS col
  3. UNION
  4. SELECT "THIS NEVER SHOWS UP" AS col FROM tbl
  5. )
  6. +------+
  7. | col |
  8. +------+
  9. | NULL |
  10. | |
  11. +------+

明确使用CAST来克服此问题:

  1. WITH RECURSIVE tbl AS (
  2. SELECT CAST(NULL AS CHAR(50)) AS col
  3. UNION SELECT "THIS NEVER SHOWS UP" AS col FROM tbl
  4. )
  5. SELECT * FROM tbl;
  6. +---------------------+
  7. | col |
  8. +---------------------+
  9. | NULL |
  10. | THIS NEVER SHOWS UP |
  11. +---------------------+

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

  1. sql server数据库性能优化之2-避免使用CTE公用表达式的递归【by zhang502219048】

    数据库优化中的一个实例,记录一下: 1. 原来用了CTE公用表达式的递归,reads高达约40万,看查询执行计划,使用了Nested Loops: 2. 优化去掉递归,改用其它方式实现,reads降低 ...

  2. 【转】CTE(公用表表达式)

    本文转自:爽朗的微笑  http://www.cnblogs.com/shuangnet/archive/2013/03/22/2975929.html 公用表表达式 (CTE) 具有一个重要的优点, ...

  3. 关于使用CTE(公用表表达式)的递归查询

    --关于使用CTE(公用表表达式)的递归查询 --CTE 的基本语法结构如下: WITH expression_name [ ( column_name [,...n] ) ] AS ( CTE_qu ...

  4. Sql Server 公用表达式(CTE)

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

  5. SqlServer:SqlServer(sql,游标,定时作业,行转列,列转行,公用表达式递归,merge合并)

    1.加载驱动: Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); DriverManager.getCo ...

  6. Sql — CTE公用表表达式和With用法总结

    CTE(Common Table Expression) 公用表表达式,它是在单个语句的执行范围内定义的临时结果集,只在查询期间有效.它可以自引用,也可在同一查询中多次引用,实现了代码段的重复利用. ...

  7. sql中with的用法(CTE公用表表达式):应用子查询嵌套,提高sql性能

    一.WITH AS的含义 WITH AS短语,也叫子查询部分(subquery factoring),定义一个SQL片断,该片断会被整个SQL语句所用到. 有时是为了让SQL语句的可读性更高些,也可能 ...

  8. 使用CTE公用表表达式的递归查询(WITH AS)

    公用表表达式 (CTE) 具有一个重要的优点,那就是能够引用其自身,从而创建递归 CTE.递归 CTE 是一个重复执行初始 CTE 以返回数据子集直到获取完整结果集的公用表表达式. 当某个查询引用递归 ...

  9. CTE(公用表表达式)

    -> 将复杂的派生表写在中间from子句中变得十分臃肿,给为维护等操作带来麻烦 -> 将这个派生表要是能提前到前面,给一个别名,后面查询的时候直接使用别名即可语法: with 表的别名 a ...

随机推荐

  1. 基于Redis分布式BitMap的应用

    一.序言 在实际开发中常常遇到如下需求:判断当前元素是否存在于已知的集合中,将已知集合中的元素维护一个HashSet,使用时只需耗时O(1)的时间复杂度便可判断出结果,Java内部或者Redis均提供 ...

  2. System.Console.WriteLine() 调用原理

    1.System.Console.WriteLine(类的实例)默认调用类的Tostring()方法.如果自定义的新类未override ToString()方法.那么调用Object.ToStrin ...

  3. vmware启动后虚拟机无法联网

    搜索services.msc打开服务 确认VMware NAT service和VMware DHCP service服务处于启动状态:开启自动启动,下次就会自动联网了. VMware Authori ...

  4. [GYCTF2020]Ezsqli 无列名注入

    手工注入了几下,是数字注入,过滤了 or , union 输入1||1=1   回显Nu1L 再输入 1&&(ascii(substr(database(),1,1))>32)# ...

  5. Leaflet:Marker、Popup类

    Marker.Popup.Tooltip类都是继承自Layer类:Event与Layer Marker 1.用例 L.marker([41,123]).addTo(map); 2.实例化 L.mark ...

  6. anaconda及jupyter notebook的使用之numpy模块的用法(2)

    今日内容概要 numpy模块结束 ndarray创建 numpy内置方法 索引与切片(花式索引.布尔索引) 常用函数 统计方法 随机数 numpy的内置方法 import numpy as np 1. ...

  7. 【python画圆】pip安装库时出现Read timed out.解决办法

    昨天第一次用python画圆,当时并没有安装numpy库(导入数据包)和matplotlib库(导入图形包),于是尝试用pip安装库 首先,我先更新了pip,如下图: 顺便附上成功截图: 然后安装nu ...

  8. 《手把手教你》系列基础篇(七十三)-java+ selenium自动化测试-框架设计基础-TestNG实现启动不同浏览器(详解教程)

    1.简介 上一篇文章中,从TestNg的特点我们知道支持变量,那么我们这一篇就通过变量参数来启动不同的浏览器进行自动化测试.那么如何实现同时启动不同的浏览器对脚本进行测试,且听宏哥娓娓道来. 2.项目 ...

  9. centos7运行system-config-kickstart时报错

    centos7运行system-config-kickstart时在Package Selection模块中出现如下错误: Package Selection Packageselection is ...

  10. laravel7 手机号验证码登陆

    1"设置路由: //展示手机登录页面 Route::get('admin','admin\AdminController@admin'); 2:html页面 <!DOCTYPE HTM ...