串联多个 CTE
构建相互引用的命名步骤处理流程
串联多个 CTE 是 CoddyKit 上的免费 Coding Interview Prep 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Coding Interview Prep 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Coding Interview Prep 课程共包含 4 节课。
为什么要串联 CTE
真实的面试题很少能一步完成。串联 CTE 可以构建一条由命名阶段组成的流水线,让每个阶段转换前一阶段的输出。这与资深工程师将复杂查询拆解成易于处理的部分时采用的方式一致。
您不必将子查询嵌套三层,而是可以一次编写每个步骤,为它命名,并让后续步骤引用它。
逗号分隔语法
要定义多个 CTE,只需写一次 WITH,然后用逗号分隔每个命名代码块。您不需要重复 WITH 关键字。
- 顶部写一个
WITH。 - 每个 CTE 定义之间使用逗号。
- 最终主查询前不要加逗号。
WITH a AS (
SELECT customer_id FROM orders
),
b AS (
SELECT customer_id FROM a
)
SELECT *
FROM b;后面的 CTE 可以引用前面的 CTE
串联的强大之处在于:一个 CTE 可以读取它之前定义的任意 CTE。这种只能向前可见的规则让您能够构建依赖链。
前面的 CTE 无法看到后面的 CTE,因此顺序很重要。请按照从原始数据到最终结构的顺序安排各个阶段。
WITH filtered AS (
SELECT *
FROM events
WHERE event_type = 'purchase'
),
per_user AS (
SELECT user_id, COUNT(*) AS purchases
FROM filtered
GROUP BY user_id
)
SELECT *
FROM per_user;实战示例:三阶段流水线
问题:在消费超过 1000 美元的客户中,平均消费金额是多少?将问题拆成三个阶段:计算每位客户的总消费金额,筛选高消费客户,然后计算这些客户的平均消费金额。
每个 CTE 名称都记录了其用途,因此审阅者可以立即理解整个流程。
WITH spend AS (
SELECT customer_id, SUM(amount) AS total
FROM orders
GROUP BY customer_id
),
big_spenders AS (
SELECT customer_id, total
FROM spend
WHERE total > 1000
)
SELECT AVG(total) AS avg_big_spend
FROM big_spenders;定义顺序很重要
由于只能向前可见,依赖另一个 CTE 的 CTE 必须列在其依赖项之后。如果引用了尚未定义的名称,数据库会引发“关系不存在”错误。
养成一个好习惯:从上到下阅读 CTE 列表,并确认每个使用的名称都已经在它上方出现。
从多个其他 CTE 引用同一个 CTE
一个 CTE 可以为多个后续 CTE 提供数据。这正是串联优于嵌套子查询的地方:您只需计算一次基础结果,然后从它分支构建后续结果。
这里的 active 和 recent 都从 base 读取数据,避免了重复编写逻辑。
WITH base AS (
SELECT * FROM users WHERE deleted = false
),
active AS (
SELECT id FROM base WHERE last_login > NOW() - INTERVAL '7 days'
),
recent AS (
SELECT id FROM base WHERE created_at > NOW() - INTERVAL '30 days'
)
SELECT (SELECT COUNT(*) FROM active) AS active_cnt,
(SELECT COUNT(*) FROM recent) AS recent_cnt;连接两个 CTE
串联的 CTE 经常会在主查询中进行连接。分别计算两侧的结果,然后将它们组合起来。这样可以让每个计算彼此隔离,同时使连接保持简单。
下面分别独立计算订单数量和退款数量,然后按客户将它们连接起来。
WITH orders_cte AS (
SELECT customer_id, COUNT(*) AS orders
FROM orders GROUP BY customer_id
),
refunds_cte AS (
SELECT customer_id, COUNT(*) AS refunds
FROM refunds GROUP BY customer_id
)
SELECT o.customer_id, o.orders, COALESCE(r.refunds, 0) AS refunds
FROM orders_cte o
LEFT JOIN refunds_cte r ON r.customer_id = o.customer_id;可读性胜过嵌套
比较一个三层嵌套子查询和一条由三个 CTE 组成的流水线。嵌套版本迫使读者在脑中从内向外逐层展开,而 CTE 版本按照执行顺序从上到下阅读。
面试官会认可 CTE 方案,因为这是他们希望在生产环境中维护的写法。为每个阶段命名就是不会过时的文档。
常见的串联错误
初学者经常在最后一个 CTE 后面、主 SELECT 前面多加一个逗号。这个尾随逗号会导致语法错误。
- 逗号只能放在 CTE 定义之间。
- 最后的右括号后面应直接接主查询,不要加逗号。
另一个陷阱是:忘记每个 CTE 都需要在括号内包含自己完整的 SELECT。
每个阶段都会单独运行吗?
这是一个细致的面试要点:从逻辑上看,流水线由彼此独立的步骤组成,但优化器可能会将它们内联并融合为一个执行计划。在大多数数据库引擎中,您通常不必为中间结果的物化付出代价。
因此,串联可以帮助您理解查询,而不一定会带来性能成本。提到这一点可以体现您的深入理解。
像流水线一样命名阶段
好的阶段名称能让查询成为自文档化代码。请优先使用描述每个步骤输出的名称,而不是描述操作的名称。
spend和big_spenders比step1和step2更好。- 读者应该仅凭 CTE 名称就能推断出完整流程。
- 各阶段使用一致的命名方式,可以让主查询中的连接一目了然。
在面试中,清晰地命名阶段能表明您编写的是易于维护的生产环境 SQL。
快速检查
请检查您对串联 CTE 如何相互引用的掌握程度。
回顾:串联 CTE
您学会了构建流水线:使用一个 WITH,用逗号分隔 CTE 定义,并采用只能向前可见的规则,让每个阶段都能读取前面的阶段。
- 按照从原始数据到最终结果的顺序排列 CTE。
- 在多个后续步骤中复用基础 CTE。
- 主查询前不要加尾随逗号。
- 串联有助于提升可读性,同时不一定会损害性能。
接下来:比较 CTE、子查询和临时表。
常见问题解答
「串联多个 CTE」课时是免费的吗?
是的 — 「串联多个 CTE」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Coding Interview Prep 课程的其余内容,请升级到 CoddyKit PRO。 Coding Interview Prep 课程共包含 4 节课。
「串联多个 CTE」这节课中我会学到什么?
构建相互引用的命名步骤处理流程 你通过在浏览器中直接运行的动手代码来练习 Coding Interview Prep,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Coding Interview Prep 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Coding Interview Prep 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「串联多个 CTE」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Coding Interview Prep 课中编写并运行代码吗?
能。每节 Coding Interview Prep 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 编写第一个 CTE
- 串联多个 CTE
- CTE、子查询与临时表
- 将嵌套查询重构为 CTE