SQL WITH 子句:公用表表达式(CTE)和递归查询

Published: 2026-02-04

WITH 子句(也称为公用表表达式,Common Table Expression,简称 CTE)允许你在查询中定义临时的具名结果集,并在后续语句中引用它们。这能让复杂查询更易读、更易复用。大多数现代 SQL 数据库都支持它,包括 SQLite、PostgreSQL、MySQL 8.0+、SQL Server 和 Oracle。

示例数据

下面所有示例都使用两张表:

employees 表:

id name manager_id salary
1 Alice NULL 120000
2 Bob 1 80000
3 Carol 1 85000
4 Dave 2 60000
5 Eve 2 62000
6 Frank 3 58000

sales 表:

id employee_id amount month
1 2 5000 January
2 2 6000 February
3 3 7000 January
4 3 8000 February
5 4 3000 January
6 5 4000 January
7 6 2500 February

基本 WITH 子句(非递归)

WITH 子句定义一个或多个具名的临时表,这些表只在当前查询执行期间存在。

示例:按员工统计总销售额

employee_sales CTE 计算每个员工的总销售额,然后我们将它与 employees 表连接以显示姓名。

多个 CTE

你可以定义多个用逗号分隔的 CTE。后面的 CTE 可以引用前面的 CTE。

示例:包含多种指标的销售分析

WITH RECURSIVE:遍历层级结构

WITH RECURSIVE 允许 CTE 引用自身,从而遍历层级或树形数据。这对组织结构图、分类层级或任何父子关系都非常有用。

一个递归 CTE 包含两部分:

  1. 基础查询(Base query):初始行集合
  2. 递归查询(Recursive query):引用 CTE 本身以查找相关行

示例:组织结构图(某个经理下的所有员工)

查找 Alice(CEO)下的所有员工:

示例:管理链(查找上级层级)

查找 Dave 的完整管理链(Dave 汇报给 Bob,Bob 汇报给 Alice):

各数据库中的 WITH RECURSIVE 语法

WITH RECURSIVE 在以下数据库中受支持:

  • SQLite — 3.8.3+(2013)
  • PostgreSQL — 8.4+(2009)
  • SQL Server — 2005+(称为 “Recursive CTE”)
  • Oracle — 11g+(称为 “Recursive Subquery Factoring”)
  • MySQL — 8.0+(2018)

注意: 较早版本的 MySQL 以及某些其他数据库不支持递归 CTE——它们可能需要使用存储过程或在应用层实现递归等其他方式。

WITH 子句的常见用法

  • 简化复杂查询 —— 将大型查询拆分为多个逻辑步骤
  • 复用子查询 —— 某个计算只定义一次,多处使用
  • 层级遍历 —— 遍历树形结构(组织架构、分类、评论)
  • 生成序列 —— 创建数字范围或日期范围
  • 渐进聚合 —— 分步骤构建聚合结果

小结

  • WITH(CTE)为查询创建可复用的具名临时结果集
  • 可以将多个 CTE 串联在一起使用
  • WITH RECURSIVE 允许 CTE 自引用,用于层级查询
  • 递归 CTE 需要一个基础部分和一个递归部分,并用 UNION ALL 连接
  • 所有现代 SQL 数据库都支持(语法可能略有差异)

可以尝试修改上面的示例,探索不同的层级结构或计算方式!