SQL——递归CTE

递归公用表达式(CTE)是SQL中用于处理递归查询的一种技术,常用于遍历层级数据。本文详细介绍了CTE的构成、执行顺序及限制,并提供了MySQL的实例,展示如何使用递归CTE遍历组织结构。在MySQL示例中,CTE首先找到最高级别的管理者,然后递归地查找所有下属,直至没有更多直接汇报的员工。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

递归公用表达式(CTE)是一个CTE,它有一个子查询,它引用CTE名称本身。

语法:

WITH RECURSIVE cte_name AS (
    initial_query  -- anchor member
    UNION ALL
    recursive_query -- 引用CTE名称的递归成员
SELECT * FROM cte_name; 

递归CTE由三个主要部分组成:

  1. 初始查询,形成CTE结构的基本结果集。初始查询部分称为锚成员。
  2. 递归查询部分是引用CTE名称的查询,因此,它被称为递归成员。递归成员由UNION ALL或UNION DISTINCT运算符与锚成员连接。
  3. 终止条件,确保递归成员不返回任何行时停止递归。

递归CTE的执行顺序如下:
4. 首先,将成员分为两部分:锚点和递归成员。
5. 接下来,执行锚成员以形成基本结果集(R0),并将此基本结果集用于下一次迭代。
6. 然后,执行带有Ri结果集作为输入的递归成员并将其Ri+1作为输出。
7. 之后,重复第三步,直到递归成员返回空结果集,换句话说,满足终止条件。
8. 最后,使用UNION ALL运算符将结果集从R0到Rn组合。

递归成员限制:
递归成员不得包含一下结构:
9. 聚合函数,例如MAX,MIN,SUM,AVG,COUNT等。
10. GROUP BY子句
11. ORDER BY子句
12. LIMIT 子句
13. DISTINCT

注意:上述约束不适用于锚点成员。此外,禁止DISTINCT仅在您使用UNION时适用。如果您使用UNION DISTINCT,DISTINCT则允许。此外,递归成员只能在其FROM子句中引用CTE名称一次,而不能在任何子查询中引用。

MySQL递归CTE实例
WITH RECURSIVE cte_count (n) 
AS (
      SELECT 1
      UNION ALL
      SELECT n + 1 
      FROM cte_count 
      WHERE n < 3
    )
SELECT n 
FROM cte_count; 

在此实例中:

SELECT 1 

是返回1作为基本结果集的锚成员。

以下查询:

SELECT n + 1
FROM cte_count 
WHERE n < 3 

是递归成员,因为它引用了CTE的名称cte_count。

n < 3 递归成员中的表达式是终止条件。一旦n等于3,递归成员将返回一个空集,该集将停止递归。

下图说明了上述CTE的要素:
在这里插入图片描述
递归CTE返回以下输出:
在这里插入图片描述

MySQL使用递归CTE遍历分层数据

使用mysqldemo示例数据库中的employees表进行演示。

在这里插入图片描述

employees表有reportsTo引用employeeNumber字段的字段。reportsTo列存储管理员的ID。最高管理者不会向公司组织结构中的任何人报告,因此,reportsTo列中的值为NULL。

可以应用递归CTE以自上而下的方式查询整个组织结构,如下所示:

WITH RECURSIVE employee_paths AS
  ( SELECT employeeNumber,
           reportsTo managerNumber,
           officeCode, 
           1 lvl
   FROM employees
   WHERE reportsTo IS NULL
     UNION ALL
     SELECT e.employeeNumber,
            e.reportsTo,
            e.officeCode,
            lvl+1
     FROM employees e
     INNER JOIN employee_paths ep ON ep.employeeNumber = e.reportsTo )
SELECT employeeNumber,
       managerNumber,
       lvl,
       city
FROM employee_paths ep
INNER JOIN offices o USING (officeCode)
ORDER BY lvl, city;

首先,使用以下查询形成锚点成员:

SELECT 
    employeeNumber, reportsTo managerNumber, officeCode
FROM
    employees
WHERE
    reportsTo IS NULL 

这个查询(锚定件)返回高级经理,其reportsTo是NULL。

其次,通过引用CTE名称来创建递归成员,employee_paths在这种情况下:

SELECT 
    e.employeeNumber, e.reportsTo, e.officeCode
FROM
    employees e
        INNER JOIN
    employee_paths ep ON ep.employeeNumber = e.reportsTo 

此查询(递归成员)返回管理器的所有直接汇报,直到没有更多的直接汇报。如果递归成员没有返回直接报告,则递归停止。

第三,使用employee_pathsCTE 的查询将CTE返回的结果集与offices表连接,以生成最终结果集。

以下是查询的输出:
在这里插入图片描述

### 高级SQL递归查询的用法 递归公用表表达式(Recursive Common Table Expressions, CTEs)是一种强大的工具,用于处理层次结构数据或迭代计算。以下是关于如何使用高级SQL递归查询的一些关键点: #### 初步概念 递归CTE由三个主要部分组成:初始集、递归调用和终止条件。这些组件共同定义了一个自引用的关系[^3]。 ```sql WITH Recursive_CTE AS ( -- Initial Set: 定义基础成员 SELECT ... FROM Base_Table WHERE ... UNION ALL -- Recursive Call: 自身引用并扩展结果集 SELECT ... FROM Recursive_CTE rcte INNER JOIN Some_Other_Table ot ON rcte.Key_Column = ot.Parent_Key_Column WHERE ... -- 终止条件通常在这里实现 ) -- 查询最终的结果 SELECT * FROM Recursive_CTE; ``` #### 实际应用案例 下面是一个更复杂的例子,展示如何利用递归来解决实际问题——例如遍历树形结构中的节点关系。 假设有一个员工表`Employees`,其中包含字段`EmployeeID`, `ManagerID`(指向其上级经理),以及一些其他属性。我们希望找到某个特定员工及其所有下属的信息。 ```sql WITH EmployeeHierarchy AS ( -- 初始化集合:从指定根节点开始 SELECT EmployeeID, ManagerID, Name, Level AS HierarchyLevel FROM Employees WHERE EmployeeID = :Root_Employee_ID UNION ALL -- 递归调用:查找当前记录的所有直接下级 SELECT e.EmployeeID, e.ManagerID, e.Name, eh.HierarchyLevel + 1 AS HierarchyLevel FROM Employees e INNER JOIN EmployeeHierarchy eh ON e.ManagerID = eh.EmployeeID ) SELECT * FROM EmployeeHierarchy; ``` 此脚本不仅返回了给定起始点下的所有子项,还附加了一列表示它们之间的相对深度级别。 #### 查看执行计划以优化性能 当构建复杂递归查询时,了解其运行机制非常重要。可以采用多种方式来分析执行计划,从而改进效率。一种方法就是借助Oracle提供的`DBMS_XPLAN.DISPLAY`函数显示详细的执行细节[^4]。 ```sql EXPLAIN PLAN FOR WITH RecursiveQuery AS (...); SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(NULL, NULL, 'ADVANCED')); ``` 上述命令会生成一份详尽报告,帮助识别潜在瓶颈所在之处。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值