SQL 中的 WITH ... AS ...

SQL 中的 WITH ... AS ... 是一种非常强大且常用的语法结构,用于定义公用表表达式 (Common Table Expression,简称 CTE)。它可以帮助你将复杂的查询拆解为更清晰、可读性更强的逻辑块。


一、基本语法

复制代码
WITH cte_name AS (
    -- 子查询(SELECT 语句)
    SELECT ...
)
SELECT * FROM cte_name;
  • cte_name:你给这个临时结果集起的名字(类似一个临时视图)。
  • AS (...):括号内是一个标准的 SELECT 查询,不能包含 INSERT、UPDATE、DELETE 等 DML 语句。
  • CTE 只在当前语句中有效,执行完就释放,不会持久化。

二、为什么用 CTE?

  1. 提高可读性:把复杂逻辑分解成多个步骤。
  2. 避免重复子查询:如果同一个子查询要用多次,用 CTE 定义一次即可。
  3. 支持递归查询:这是 CTE 最强大的特性之一(如查询组织架构、树形结构等)。

三、简单示例

假设有一张员工表 employees(id, name, manager_id, salary):

示例 1:非递归 CTE

找出工资高于平均工资的员工:

复制代码
WITH avg_salary AS (
    SELECT AVG(salary) AS avg_sal FROM employees
)
SELECT e.name, e.salary
FROM employees e
JOIN avg_salary a ON e.salary > a.avg_sal;

这里 avg_salary 是一个只含一行一列的临时表,供主查询使用。


示例 2:多个 CTE(用逗号分隔)

复制代码
WITH 
high_earners AS (
    SELECT id, name, salary
    FROM employees
    WHERE salary > 100000
),
dept_counts AS (
    SELECT department_id, COUNT(*) AS emp_count
    FROM employees
    GROUP BY department_id
)
SELECT h.name, h.salary, d.emp_count
FROM high_earners h
JOIN employees e ON h.id = e.id
JOIN dept_counts d ON e.department_id = d.department_id;

多个 CTE 之间用逗号分隔,最后一个 CTE 后直接跟主查询。


四、递归 CTE(Recursive CTE)

用于处理层级结构 或树形数据,比如:公司组织架构、评论的父子关系、物料清单等。

递归 CTE 语法:

复制代码
WITH RECURSIVE cte_name AS (
    -- 初始查询(锚点成员)
    SELECT ...
    UNION ALL
    -- 递归查询(引用自身)
    SELECT ...
    FROM cte_name
    JOIN ...
)
SELECT * FROM cte_name;

注意:有些数据库(如 PostgreSQL、MySQL 8.0+、SQL Server)需要写 WITH RECURSIVE,而有些(如 SQL Server)只需 WITH 即可自动识别递归。

示例:查询某员工的所有下属(多级)

复制代码
WITH RECURSIVE subordinates AS (
    -- 锚点:从某个经理开始(比如 id = 1)
    SELECT id, name, manager_id, 0 AS level
    FROM employees
    WHERE id = 1

    UNION ALL

    -- 递归:找下一级员工
    SELECT e.id, e.name, e.manager_id, s.level + 1
    FROM employees e
    INNER JOIN subordinates s ON e.manager_id = s.id
)
SELECT * FROM subordinates;

这会返回 ID 为 1 的员工及其所有直接/间接下属,并标注层级深度。


五、注意事项

数据库 是否支持 CTE 是否需要RECURSIVE关键字
PostgreSQL ✅ 需要(递归时)
MySQL 8.0+ ✅ 需要(递归时)
SQL Server ✅ 不需要(自动识别)
Oracle ✅ 不需要(但可用)
SQLite 3.8.3+ ✅ 需要(递归时)
旧版 MySQL(<8.0) ❌ 不支持

六、CTE vs 子查询 vs 临时表

特性 CTE 子查询 临时表
可读性 ⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐
性能 通常与子查询相当(优化器会重写) 同左 可能更快(可加索引)
作用域 仅当前语句 仅当前查询块 当前会话
支持递归 ✅ ❌ ❌(除非手动循环)
可重复引用 ✅(多次) ❌(需复制) ✅

七、总结

  • WITH ... AS ... 定义的是临时命名结果集(CTE)。
  • 适用于简化复杂查询、提升可读性、实现递归逻辑。
  • 在现代 SQL 开发中几乎是必备技能。
相关推荐
SelectDB技术团队7 分钟前
VARIANT 列接入网关 JSON 日志:建表、导入、子列查询与物化的完整步骤
大数据·数据库·人工智能·elasticsearch·搜索引擎·json·apache doris
涛思数据(TDengine)12 分钟前
TDengine IDMP 2.0.2 发布:元素关系可视化、数据建模与联邦查询三大升级
大数据·数据库·人工智能·时序数据库·tdengine·工业互联网·工业ai
小米里的大麦13 分钟前
17 MySQL 视图
数据库·mysql
ss27318 分钟前
PG实战笔记 | 07 PostgreSQL常见编译项及其作用
数据库·笔记·postgresql
丨白色风车丨33 分钟前
LangChain 1.0 核心用法速查:创建模型、创建 Agent、调用
数据库·langchain
临床数据科学和人工智能兴趣组35 分钟前
正则表达式的基础语法的常用字符类
数据库·r语言·r语言-4.2.1·临床研究·临床试验
鱼很腾apoc37 分钟前
【MySQL】 第2期 查询与函数系统详解:从基础 CRUD 到复合多表查询
linux·数据库·学习·mysql·dml
前端 贾公子1 小时前
LangGraph == 图的状态(State)管理 (下)
运维·服务器·数据库
zlwool1 小时前
机械行业ERP与进销存的合规选型边界
数据库·erp
梦想的颜色1 小时前
【AI速览】RAG 向量数据库选型:Milvus、Chroma、Qdrant、PGVector 全维度横向对比
数据库·milvus·chroma·rag·pgvector·向量库·qdrant