一、什么是CTE 全称 common table expressions,表示临时结果,用with as语句,可以在其他SQL中引用,如select、insert、update和delete。 参考 1、PostgreSQL CTE语句与materialized 2、7.8. WITH查询(公共表表达式)
Good Day~ 今天聊个Oracle的CTE,刚听到这个会不会有点陌生?CTE是英文Common Table Expressions(公共表表达式)。 首先CTE的原理是生成SQL语句运行范围内的临时视图,以让这个临时视图可以被反复运用。 聊另一个核心功能~递归层级显示,也就是如果你有多个视图,你可以通过CTE非常简单的实现业务的递归查询。 上个代码来看看~ 递归层级显示是CTE非常重要的一点,让我们来详细聊聊每个步骤~这个递归CTE查询的执行分为以下几个关键阶段:1. 这种机制使得递归CTE非常适合处理树形或层次结构数据,比传统的CONNECT BY语法更符合SQL标准且更灵活。 讲一百遍不如实践一遍,你看懂了吗?
with cte as ( select Id,Pid,DeptName,0 as lvl from Department where Id = 2 union all select d.Id,d.Pid,d.DeptName,lvl+1 from cte c inner join Department d on c.Id = d.Pid ) select * from cte 1 表结构 Id Pid DeptName ----------- ----------- ----------------------------- 5 美工 2 (5 行受影响) 3 原理(摘自网上) 递归CTE 第二个查询被称为递归成员,使该查询称为递归成员的是对CTE名称的递归引用是触发。在逻辑上可以将CTE名称的内部应用理解为前一个查询的结果集。
一、递归查询 1.结构: CTE的递归查询必须满足三个条件:初始条件,递归调用表达式,终止条件,CTE 递归查询的伪代码如下: WITH cte_name ( column_name [,...n] 第二个查询被称为递归子查询成员:该子查询调用CTE名称,触发递归查询,实际上是递归子查询调用递归子查询。 在逻辑上可以将CTE名称的内部应用理解为前一个查询的结果集。 3.递归步骤: step1:定点子查询设置CTE的初始值,即CTE的初始值Set0;递归调用的子查询过程:递归子查询调用递归子查询; step2:递归子查询第一次调用CTE名称,CTE名称是指CTE 的初始值Set0,第一次执行递归子查询之后,CTE名称是指结果集Set1; step3:递归子查询第二次调用CTE名称,CTE名称是指Set1,第二次执行递归子查询之后,CTE名称是指结果集Set2 在查询语句中调用中cte,而查询语句就是cte的组成部分,即 “自己调用自己”,这就是递归的真谛所在。
CTE 的主要目的是将复杂的查询逻辑分解为多个简单、可读的步骤,从而提升 SQL 代码的结构化程度和可维护性。 CTE 可以分为两种类型:非递归 CTE 和递归 CTE。 三、 非递归 CTE 非递归 CTE 是 CTE 的基础形式,本质上是一个可以复用的子查询。 3.1 语法 要指定公共表表达式,请使用 WITH 子句,该子句包含一个或多个以逗号分隔的子句。 JOIN cte2 WHERE cte1.a = cte2.c; 在使用了 WITH 子句的语句里,可以通过每个 CTE 的名字来查询它所生成的结果集。 递归 CTE 是一种特殊的 CTE,它的子查询会引用其自身的名称,从而实现循环遍历。 4.1 语法 如果一个 CTE 的子查询引用了其自身的名称,那么该 CTE 就是递归的。 如果 WITH 子句中的任何 CTE 是递归的,则必须包含 RECURSIVE 关键字。
在 SQL 中,WITH AS 语法通常用于创建 公用表表达式(CTE, Common Table Expression)。它可以让你的查询结构更清晰、更容易阅读,尤其适合处理复杂的多层嵌套查询。 FROM 临时表名; 说明: WITH 后面定义一个或多个临时表(即 CTE)。 每个临时表必须有 名称,并用 AS 指定它的查询内容。 定义完后,就可以在主查询中像普通表一样使用这个临时表。 三、多个 CTE 示例 你可以同时定义多个临时表。 示例(高级用法) 递归 CTE 用于层级结构查询(比如组织架构、目录树等)。 注意 RECURSIVE 它是数据库的关键词,用于指定 CTE 的类型(递归型)。 五、小结 项目 作用 WITH ... AS (...)
代码复用:一个 CTE 可以在同一查询中被多次引用。 支持递归查询(虽然 HQL 对递归 CTE 的支持可能有限或有特定语法)。 语法: WITH cte_name1 AS ( SELECT_statement1 ), cte_name2 AS ( SELECT_statement2 -- 可以引用 cte_name1 ) SELECT FROM cte_name1 JOIN cte_name2 ON ... WHERE ...; 示例:计算每个部门的平均工资,然后找出工资高于其所在部门平均工资的员工。 练习题3 (使用 CTE): 查询每个班级的最高分,并列出获得该班级最高分的学生姓名、分数以及班级名称。 练习题8 (CTE 与聚合): 找出每个客户 (customer_id) 的订单总数和总订单金额。
R.DocStatus=30 where S.UserType=3 该查询需要执行10秒左右,仔细分析,它有2次查询类似的结果集(Base_Staff,Rpt_RegistForm 关联部分),这正是CTE 下面看看经过CET改写过的查询: With CTE as ( select --s.Id as S_ID, s.Name ,s.AccountantCode, on b.Id =CTE.BusinessBackupCustomerId where t0.AccountantCode=CTE.AccountantCode ) t1 ) as '约定书数 ' from ( select Name, AccountantCode,COUNT( BusinessBackupCustomerId) as '报告数' from CTE group by Name 另外,CTE还可以做递归处理,详细见上面的联机丛书URL的内容说明。
通用表表达式(CTE)非常适合清理代码。但递归CTE是完全不同的野兽。它们允许你在CTE自己的定义中引用CTE本身。这听起来像是无限循环的领域(确实可能!) 生成日历日期以填补报告空白在图数据中查找路径(航班连接、网络路由)递归CTE流程图:展示从锚点成员到递归成员再到终止条件的完整流程WITHRECURSIVE的解剖递归CTE始终包含三个部分:锚点成员(AnchorMember ):起点(非递归)递归成员(RecursiveMember):引用CTE名称的查询终止条件(TerminationCondition):最终停止递归的WHERE子句基础语法示例:展开代码语言:SQLAI 递归CTE是在PostgreSQL和SQLite中执行此操作的标准方法。 关键要点:递归CTE由三部分组成:锚点、递归和终止条件适用于层级数据、日期序列、图遍历等场景始终包含终止条件以避免无限循环对于大型数据集,考虑性能优化跨数据库支持良好,但语法略有差异何时使用递归CTE:
作者:Guilhem Bichot 译:徐轶韬 在MySQL 8.0.1中,我们引入了对递归通用表表达式(CTE)的支持。 Try increasing @@cte_max_recursion_depth to a larger value.” 在这种情况下,增加变量不会有任何改善。 尽管这只是一个小示例,但CTE可以永远递归还有其他原因:查询可能非常复杂,我们犯了逻辑错误;或数据集可能是格式错误的层次结构,并且包含意外的循环。 从版本8.0.19开始,我使它允许任何递归CTE包含LIMIT子句。因此,递归算法将开始工作,照常运行迭代,累积行,并在这些行的数量超过LIMIT时停止。 在本文的结尾,虽然LIMIT-in-CTE可能不会改变SQL 的面貌,但我相信它几乎可以为在MySQL中操作递归CTE的每个人节省时间,这是一件非常好的事情! 一如既往,感谢您选择MySQL!
目录 1. 从根遍历到叶 2. 从叶遍历到根 3. 确定叶子节点、分支节点和根节点 (1)使用相关子查询 (2)更高效的写法(一次外连接) ---- 表数据: mysql> select * from t1; +------+------+ | id | pid | +------+------+ | 7788 | 7566 | | 7902 | 7566 | | 7499 | 7698 | | 7521 | 7698 | | 7900 | 7698 | | 7844 | 7698 | | 7654
CTE用法 (1)最基本的CTE语法如下 mysql> with -> cte1 as (select * from sbtest1 where id in (1,2)), -> cte2 as (select * from sbtest1 where id in (2,3)) -> select * from cte1 join cte2 where cte1.id=cte2. ,可以被另一个CTE引用,具体如下: mysql> with -> cte1 as (select * from sbtest1 where id=1), -> cte2 as (select * from cte1) -> select * from cte2; +----+---------+-------------------------------------------- 其实不是的,虽然CTE内部优化流程与Derived Table类似,但是两者还是区别的,具体如下: (1)一个CTE可以引用另一个CTE (2)CTE可以自引用 (3)CTE在语句级别生成临时表,多次调用只需要执行一次
我们知道,CTE是不可以使用Order BY的,那么我们有什么方法可以通过类似方法实现Order By的功能呢? 示例 With Base AS ( SELECT ... ...
我还将演示 CTE 是如何处理递归逻辑并定义递归 CTE 的运行方式的。 接着跟随 CTE 其后的是通过列别名引用 CTE 的 SELECT 语句。 理解 CTE 在设计 CTE 之前,必须理解它的工作原理和遵循的规则。 CTE 仅能被紧随其后的语句所引用。这意味着如果要使用 CTE,则必须紧随 T-SQL 批处理中的 CTE 之后编写引用 CTE 的查询。 此外,CTE 后面也可以跟随另一个 CTE。在想要把中间结果聚集到行集时,可使用这种技术从其他 CTE 构建 CTE。当创建从其他 CTE 构建的 CTE 时,请用逗号分隔 CTE 的定义。 每个 CTE 都可以被紧随其后的 CTE 所引用,形成层接的构建关系。CTE 定义后面的数据操作语言 (DML) 语句也可引用 WITH 子句中定义的任何 CTE。
b, d FROM cte1 JOIN cte2 WHERE cte1.a= cte2.c; 看起来是不是像是将派生表放在了前面? FROM d AS d1 JOIN d AS d2 ON d1.b = d2.a; 也可以在其他CTE中引用CTE名称,从而使CTE能够基于其他CTE进行定义。 CTE。 递归CTE常见于生成序列,层次或树状结构的遍历。 而CTE不论使用了几次参照,仅物化一次。
parent_id关联实现向下钻取level字段记录当前层级深度场景2:路径追踪 - 生成完整部门路径需求:为每个部门生成形如"总公司/技术部/后端组"的完整路径WITH RECURSIVE path_cte UNION ALL SELECT d.id, d.name, CONCAT(pc.path, '/', d.name) FROM departments d JOIN path_cte pc ON d.parent_id = pc.id)SELECT * FROM path_cte;关键技巧:使用CAST初始化路径类型CONCAT拼接父路径和当前节点结果包含所有可能的路径分支场景3: 将父节点编号与当前节点ID拼接 FROM departments d INNER JOIN dept_tree dt ON d.parent_id = dt.id -- 通过父ID连接到递归CTE WHERE条件中使用函数及时终止已完成遍历的分支循环引用检测 添加cycle id SET is_cycle USING path防止死循环(MySQL 8.0.19+)结语通过这五个实战场景,我们已经解锁了CTE
,从而创建递归 CTE。 递归 CTE 是一个重复执行初始 CTE 以返回数据子集直到获取完整结果集的公用表表达式。 当某个查询引用递归 CTE 时,它即被称为递归查询。 --运行 CTE 的语句为: SELECT <column_list> FROM expression_name; 在使用CTE时应注意如下几点: CTE后面必须直接跟使用CTE的SQL语句( CTE后面也可以跟其他的CTE,但只能使用一个with,多个CTE中间用逗号(,)分隔,如下面的SQL语句所示: with cte1 as ( select * from table1 where 如果CTE的表达式名称与某个数据表或视图重名,则紧跟在该CTE后面的SQL语句使用的仍然是CTE,当然,后面的SQL语句使用的就是数据表或视图。 4.
example.com'), -- 重复数据 ('Charlie', 'charlie@example.com'), ('Bob', 'bob@example.com'); -- 重复数据 巧用 CTE ---+---------+---------------------+ 3 rows in set (0.00 sec) 解读: 这个SQL语句可以分为两个主要部分 1)WITH子句(公共表表达式,CTE CREATE TEMPORARY TABLE IF NOT EXISTS temp_duplicates ( id INT PRIMARY KEY ); -- 使用CTE 查找并插入重复记录 使用 CTE (Common Table Expression) duplicates 找出 users 表中的重复记录。
CTE 与派生表类似,具体表现在不存储为对象,并且只在查询期间有效。与派生表的不同之处在于,CTE 可自引用,还可在同一查询中引用多次。 CTE的使用 CTE使语句更加简洁 例如以下两个语句表达的是同一语义,使用CTE比未使用CTE的嵌套查询更简洁明了。 1) 使用嵌套子查询 ? 2) 使用CTE ? CTE 可以进行树形查询 ? PostgreSQL PostgreSQL从8.4开始支持CTE,PostgreSQL还扩展了CTE的功能, CTE的query中支持DML语句,例如 ? ? MariaDB MariaDB从10.2开始支持CTE。10.2.1 支持non-recursive CTE, 10.2.2开始支持recursive CTE。 目前的GA的版本是10.1. ,CTE会解析多次,因此此版本CTE有简化SQL的作用,但效率上没有效提高。
公用表表达式 CTE有两种用法,非递归的CTE和递归的CTE。 非递归的CTE可以用来增加代码的可读性,增加逻辑的结构化表达。 平时我们比较痛恨一句sql几十行甚至上上百行,根本不知道其要表达什么,难以理解,对于这种SQL,可以使用CTE分段解决, 比如逻辑块A做成一个CTE,逻辑块B做成一个CTE,然后在逻辑块A和逻辑块B Try increasing @@cte_max_recursion_depth to a larger value. 由参数@@cte_max_recursion_depth决定。 关于CTE的限制,跟其他数据库并无太大差异,比如CTE内部的查询结果都要有字段名称,不允许连续对一个CTE多次查询等等,相信熟悉CTE的老司机都很清楚。