SQL关键字三分钟入门:WITH —— 公用表表达式让复杂查询更清晰

在实际的数据库开发和分析中,我们常常会遇到复杂的多层嵌套查询,这样的 SQL 语句不仅难以阅读,也容易出错。

这时候就需要使用一个非常实用又优雅的关键字 ------ WITH

它可以帮助我们将复杂的子查询提取出来并命名 ,从而提升代码可读性、复用性和维护性。这个功能也被称为 CTE(Common Table Expressions,公用表表达式)


1.什么是 WITH?

WITH 是 SQL 中用于定义临时结果集的关键字。这些临时结果集可以在后续查询中像普通表一样被引用,并且只在当前查询执行期间存在。

你可以把它理解为:"先写好一个中间结果,后面可以直接拿来用"。


2.基本语法

sql 复制代码
WITH cte_name AS (
    -- 子查询内容
    SELECT ...
)
-- 后续主查询中使用 cte_name
SELECT * FROM cte_name;
  • cte_name 是你给中间结果集起的名字。
  • 可以定义多个 CTE,用逗号分隔。

3.示例讲解

假设我们有一个 orders 表,记录了订单信息:

order_id customer_id amount
1 1 2999
2 2 499
3 1 199
4 3 899

示例1:计算每个客户的订单总金额(简单 CTE 使用)

sql 复制代码
WITH customer_totals AS (
    SELECT customer_id, SUM(amount) AS total_amount
    FROM orders
    GROUP BY customer_id
)
SELECT *
FROM customer_totals
WHERE total_amount > 500;
结果:
customer_id total_amount
1 3198
3 899

👉 这里我们先定义了一个 CTE customer_totals 来计算每位客户的总消费金额,然后主查询筛选出金额大于500的客户。


示例2:多个 CTE 的使用(分步处理复杂逻辑)

sql 复制代码
WITH
-- 第一步:统计每位客户的总消费
customer_totals AS (
    SELECT customer_id, SUM(amount) AS total_amount
    FROM orders
    GROUP BY customer_id
),
-- 第二步:根据总消费划分客户等级
customer_levels AS (
    SELECT customer_id, total_amount,
    CASE
        WHEN total_amount > 1000 THEN '高级客户'
        WHEN total_amount BETWEEN 500 AND 1000 THEN '中级客户'
        ELSE '普通客户'
    END AS level
    FROM customer_totals
)
-- 最终查询:展示客户等级信息
SELECT * FROM customer_levels;
结果:
customer_id total_amount level
1 3198 高级客户
2 499 普通客户
3 899 中级客户

通过多个 CTE 分步骤处理,整个查询逻辑更加清晰易懂。


示例3:递归 CTE(以员工层级为例)

递归 CTE 是 WITH 的一种高级用法,常用于处理树形结构或层级数据(如组织架构、分类目录等)。

假设我们有一个 employees 表:

employee_id name manager_id
1 张三 NULL
2 李四 1
3 王五 2
sql 复制代码
WITH RECURSIVE employee_hierarchy AS (
    -- 初始查询:没有上级的员工(即 CEO)
    SELECT employee_id, name, manager_id, CAST(name AS TEXT) AS hierarchy_path
    FROM employees
    WHERE manager_id IS NULL

    UNION ALL

    -- 递归部分:查找下属员工
    SELECT e.employee_id, e.name, e.manager_id,
           CAST(eh.hierarchy_path || ' → ' || e.name AS TEXT)
    FROM employees e
    INNER JOIN employee_hierarchy eh ON e.manager_id = eh.employee_id
)
SELECT * FROM employee_hierarchy;
结果:
employee_id name manager_id hierarchy_path
1 张三 NULL 张三
2 李四 1 张三 → 李四
3 王五 2 张三 → 李四 → 王五

这个例子展示了如何用递归 CTE 构建一个组织层级路径,非常适合处理树状结构数据。


对比项 使用 WITH(CTE) 不使用 CTE(嵌套子查询)
可读性 更高,结构清晰 较低,嵌套多层时难读
复用性 可多次引用 每次都要重复写
调试方便性 易于单独测试每个 CTE 难以调试嵌套部分
递归支持 支持(RECURSIVE) 不支持
性能 与子查询基本一致,但逻辑优化后可能更好 视具体实现而定

4. 总结对比表

功能 SQL 示例
定义单个 CTE WITH cte AS (...) SELECT * FROM cte;
定义多个 CTE WITH a AS (...), b AS (...) SELECT * FROM a JOIN b...
递归 CTE WITH RECURSIVE ...
提高代码可读性 将复杂查询拆分为多个逻辑块
支持重用 同一查询中可多次引用 CTE 名
相关推荐
程序员二叉2 分钟前
【JUC】ThreadLocal底层原理|内存泄漏|弱引用|跨线程传递方案
java·开发语言·面试·职场和发展·juc
程序员二叉4 分钟前
【JUC】线程池全套深度详解|参数|流程|拒绝策略|调优|异常处理
java·开发语言·jvm·算法·面试·juc
老马识途2.011 分钟前
在AI的帮助下理解spring的启动过程
java·前端·spring
青山木19 分钟前
Hot 100 --- 轮转数组
java·数据结构·算法
阿狸猿40 分钟前
论 NoSQL 数据库技术及其应用
数据库·nosql
FBI HackerHarry浩1 小时前
DataGrip2023.2.3默认保存的数据库和.sql文件在哪里?怎么修改默认路径?
数据库
袁小皮皮不皮1 小时前
3.HCIP OSPF补充知识(优化版)
服务器·网络·数据库·网络协议·智能路由器
Qt程序员1 小时前
掌握 Linux 内核调度:从原理到实现(进程篇)
java·开发语言
运筹vivo@1 小时前
Python ContextVar 底层机制与内存模型拆解
前端·数据库·python
code bean1 小时前
【LangChain】检索器完全指南:从向量检索到生产级 RAG 架构
java·开发语言·微服务