0Pricing
Coding Interview Prep · 课时

锚成员与递归成员

了解递归 CTE 的两部分结构,以及终止机制的工作方式

锚成员与递归成员 是 CoddyKit 上的免费 Coding Interview Prep 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Coding Interview Prep 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Coding Interview Prep 课程共包含 4 节课。

为什么会考递归 CTE

当面试官给您一张组织架构图、一份物料清单或一棵分类树,并要求您找出所有后代节点时,他们是在测试您是否会想到使用递归 CTE。普通连接只能遍历固定层数;递归则可以遍历任意深度。

题目中的提示性短语通常是“任意深度”或“一直向下”。这就是您的信号。在本课中,您将学习每个递归 CTE 都具备的两部分结构:锚点成员和递归成员。

两部分骨架

递归 CTE 始终包含关键字 WITH RECURSIVE(PostgreSQL、SQLite、MySQL 8+;SQL Server 省略 RECURSIVE),其主体由两个通过 UNION ALL 组合的查询组成:

  • 锚点成员 — 起始行,只运行一次。
  • 递归成员 — 引用 CTE 自身的名称,反复运行。

请记住这个骨架;面试官很喜欢要求您从头写出它。

WITH RECURSIVE cte AS (
    -- anchor member
    SELECT ...
    UNION ALL
    -- recursive member
    SELECT ... FROM cte JOIN ...
)
SELECT * FROM cte;

锚点的作用

锚点成员是一个不引用 CTE 的普通查询。它生成种子行,也就是第 0 层的起点。对于组织架构图,通常是 CEO(经理为 NULL 的那一行);对于数字序列,则是第一个数字。

锚点恰好运行一次。它的输出会成为传入递归步骤的第一批行。

-- Anchor: the top of the hierarchy
SELECT id, name, manager_id, 1 AS depth
FROM employees
WHERE manager_id IS NULL

递归成员的作用

递归成员通过名称引用 CTE。在每次迭代中,它会将上一次迭代产生的行与基础表连接起来,从而找出下一层。

它看不到目前为止的整个 CTE,只能看到紧接着前一步新增的行。这是面试官重点考察的关键思维模型。

-- Recursive: children of the rows found so far
SELECT e.id, e.name, e.manager_id, c.depth + 1
FROM employees e
JOIN cte c ON e.manager_id = c.id

组合起来

使用 UNION ALL 将锚点成员和递归成员组合起来,数据库引擎就会自动进行迭代。每次运行都会追加下一层,直到递归成员返回零行,此时递归停止。

下面是一个完整且可运行的组织架构遍历示例,同时还会跟踪 depth。

WITH RECURSIVE org AS (
    SELECT id, name, manager_id, 1 AS depth
    FROM employees
    WHERE manager_id IS NULL
    UNION ALL
    SELECT e.id, e.name, e.manager_id, o.depth + 1
    FROM employees e
    JOIN org o ON e.manager_id = o.id
)
SELECT id, name, depth FROM org ORDER BY depth, id;

递归如何终止

当递归成员生成没有新行时,递归就会停止。无需显式的循环计数器 — 到达树的叶节点后,连接自然就无法继续产生结果。

在组织架构示例中,到达没有直接下属的员工后,下一次迭代的连接找不到子节点,返回空结果,引擎便会停止。理解这种自动终止行为是一个经典的追问点。

UNION ALL 与 UNION

面试官经常会问,为什么使用 UNION ALL 而不是 UNION。原因有两个:

  • 性能 — UNION 会在每次迭代中去重,代价很高。
  • 正确性 — 在树结构中通常不会出现重复行,因此去重只是浪费工作。

只有当结构是图,并且您确实想要合并重复节点时,才使用 UNION — 但为了防止循环,显式的保护条件更好(后文会介绍)。

跟踪深度和路径

增加两列可以让递归结果实用得多,面试中也经常会被要求这样做:

  • 深度 — 在锚点中从 1 开始,在递归成员中加 1。
  • 路径 — 累积标识符或名称组成的链路,以便查看从根节点到当前节点的路线。

将 path 构建为字符串,稍后还可以将其作为检测循环的工具。

WITH RECURSIVE org AS (
    SELECT id, name, manager_id, 1 AS depth,
           CAST(name AS VARCHAR(1000)) AS path
    FROM employees WHERE manager_id IS NULL
    UNION ALL
    SELECT e.id, e.name, e.manager_id, o.depth + 1,
           o.path || ' > ' || e.name
    FROM employees e JOIN org o ON e.manager_id = o.id
)
SELECT name, depth, path FROM org;

列类型必须匹配

有一个容易忽略的问题:锚点成员和递归成员必须返回相同数量的列,并且列的类型兼容。如果您要构建 path 字符串,锚点中的初始值必须转换为长度足够大的类型(例如 VARCHAR(1000)),否则引擎可能会截断值,或在后续迭代中抛出类型不匹配错误。

面试官正是会埋下这种细节,来判断您是否真正运行过递归 CTE,而不只是读过相关介绍。

物料清单示例

同一个骨架也能解决物料清单问题:给定一个零件,列出任意深度的所有子零件。锚点选择顶层组件;递归成员沿着从 parent_part 到 child_part 的连接进行遍历。

请注意,其结构与组织架构图完全相同 — 只有列名称发生了变化。认识到同一个骨架可以适用于许多问题,才是真正的面试技能。

WITH RECURSIVE bom AS (
    SELECT child_part, parent_part, 1 AS lvl
    FROM parts WHERE parent_part = 'ENGINE'
    UNION ALL
    SELECT p.child_part, p.parent_part, b.lvl + 1
    FROM parts p JOIN bom b ON p.parent_part = b.child_part
)
SELECT child_part, lvl FROM bom;

方言说明

面试官很欣赏下面这份简明的跨方言速查表:

  • PostgreSQL、SQLite、MySQL 8+:WITH RECURSIVE name AS (...)。
  • SQL Server:只需使用 WITH name AS (...) — RECURSIVE 关键字是隐式的,并且默认将 MAXRECURSION 限制为 100。
  • Oracle:同时支持递归 CTE 和较旧的 CONNECT BY 语法。

说出“SQL Server 不使用 RECURSIVE 这个词”,就能体现出真正的知识广度。

快速检查

请测试您对这两部分结构的掌握程度。

回顾

您现在已经掌握了递归 CTE 的骨架:

  • WITH RECURSIVE + 锚点 + UNION ALL + 递归成员。
  • 锚点提供第 0 层的种子,并运行一次。
  • 递归成员将上一次迭代与基础表连接,并持续运行,直到返回零行。
  • 使用 UNION ALL,跟踪 depth 和 path,并确保列类型兼容。

下一步:使用这个骨架向下和向上遍历真实的组织架构图。

常见问题解答

「锚成员与递归成员」课时是免费的吗?

是的 — 「锚成员与递归成员」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Coding Interview Prep 课程的其余内容,请升级到 CoddyKit PRO。 Coding Interview Prep 课程共包含 4 节课。

「锚成员与递归成员」这节课中我会学到什么?

了解递归 CTE 的两部分结构,以及终止机制的工作方式 你通过在浏览器中直接运行的动手代码来练习 Coding Interview Prep,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Coding Interview Prep 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Coding Interview Prep 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。

「锚成员与递归成员」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Coding Interview Prep 课中编写并运行代码吗?

能。每节 Coding Interview Prep 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 锚成员与递归成员
  2. 遍历组织架构图
  3. 生成数字和日期序列
  4. 避免无限递归
← 返回 Coding Interview Prep