使用 SELF JOIN 处理层级关系
将表连接到自身,以表示员工与经理、父级与子级之间的关系
使用 SELF JOIN 处理层级关系 是 CoddyKit 上的免费 SQL Interview Prep 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 SQL Interview Prep 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 SQL Interview Prep 课程共包含 4 节课。
SELF JOIN 到底是什么
自连接其实就是一种连接,其中同一张表出现在连接的两侧。SQL 没有特殊的 SELF JOIN 关键字;您只需编写普通的 INNER 或 LEFT JOIN,并两次引用同一张表。
让它能够工作的关键是表别名。您需要为同一张表的两个副本分别指定不同的别名,这样数据库引擎才会将它们视为两张彼此独立的表。
SELECT e.name, m.name AS manager
FROM employees e
JOIN employees m ON e.manager_id = m.id;为什么别名不可或缺
如果没有不同的别名,查询就会产生歧义:每个列名都会出现两次,数据库引擎无法判断您指的是哪个副本。为每个实例指定别名即可解决这个问题。
请将这个连接理解为:“将每一行员工记录与作为其经理的员工记录配对。” 别名 e 表示员工,m 表示经理,而二者都来自同一张实际表。
-- e = the employee, m = that employee's manager
SELECT e.id, e.name, m.name AS reports_to
FROM employees AS e
JOIN employees AS m ON e.manager_id = m.id;员工—经理模型
自连接最经典的场景是邻接表:一张表存储所有行,每一行通过指向同一张表的外键指向其父级。
带有 manager_id 的 employees 表通过引用 employees.id,就能在一张表中表示完整的组织结构。每位经理其实只是另一行员工记录。
-- One table holds the whole hierarchy
-- employees(id, name, manager_id)
-- manager_id -> employees.id列出每个人及其经理
最常见的自连接问题是:显示每名员工及其经理的姓名。请根据 e.manager_id = m.id,将员工副本与经理副本连接起来。
这会为每名经理存在的员工返回一行。请注意,组织结构最顶层的 CEO 没有经理,其经理编号为 NULL,因此会被 INNER JOIN 排除。
SELECT e.name AS employee, m.name AS manager
FROM employees e
JOIN employees m ON e.manager_id = m.id;使用 LEFT JOIN 保留树的顶层节点
若要包含 CEO(其 manager_id 为 NULL),请改用 LEFT JOIN。员工一侧会被保留;对于没有父级的行,经理相关列会返回 NULL。
面试官会借此测试您是否记得:INNER JOIN 自连接会丢弃根节点。解决方法与所有“保留不匹配行”的外连接场景相同。
SELECT e.name AS employee,
COALESCE(m.name, '(top level)') AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;统计每位经理的直接下属人数
一个常见的后续问题是:每位经理直接管理多少人?先执行自连接,然后按经理分组。
我们将员工与经理连接,按经理的身份分组,并统计员工数量。这里统计的只是直接下属,不包括其下方整个子树中的人员。
SELECT m.name AS manager, COUNT(*) AS direct_reports
FROM employees e
JOIN employees m ON e.manager_id = m.id
GROUP BY m.id, m.name
ORDER BY direct_reports DESC;深入两级层级
如果要获取员工、其经理以及经理的经理,请将这张表复制三份并依次连接。每增加一个层级,就需要再进行一次自连接。
这种方法适用于固定且已知的深度。如果需要处理任意深度,仅靠自连接是不够的,这时就应使用递归 CTE,面试官也希望您能提到这一点。
SELECT e.name AS employee,
m.name AS manager,
g.name AS grand_manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id
LEFT JOIN employees g ON m.manager_id = g.id;自连接与递归 CTE
面试官会考察的关键区别:
- 自连接处理的是固定数量的层级。复制三份表就表示三层,不能再多。
- 递归 CTE可以处理无限深度:它会反复将表与自身连接,直到不再出现新行。
因此,“显示每名员工及其直接经理”使用自连接即可,但“列出沿关系链向上的所有祖先”则需要递归。
父子分类
同样的模式可以表示任何树形结构:商品分类、评论线程和地理区域。一张通过 parent_id 引用自身 id 的 categories 表,与员工—经理场景的结构完全相同。
认识到“带有自引用外键的表”意味着“使用自连接或递归”,就是一种可复用的洞察。
SELECT c.name AS category,
p.name AS parent_category
FROM categories c
LEFT JOIN categories p ON c.parent_id = p.id;自连接中的常见错误
面试中请注意以下问题:
- 忘记使用别名,从而导致列歧义错误。
- 使用
INNER JOIN,却悄悄丢弃根行(父级为 NULL)。 - 连接方向错误:使用
e.id = m.manager_id,而不是e.manager_id = m.id。
在编写 ON 之前,请始终明确说出哪个别名代表子级,哪个代表父级。
何时使用自连接
当一张表中的行与同一张表中的其他行存在关系时,就应考虑使用自连接:
- 只有一个固定查询层级的层次结构(员工到经理)。
- 配对或比较同一张表中的行(下一课将介绍)。
如果这种关系是递归且无界的,请指出递归 CTE 是更合适的工具。这一细微区别可以体现初级开发人员与中级开发人员之间的差距。
快速检查
测试您对层次结构自连接的掌握程度。
要点回顾:层次结构中的 SELF JOIN
要点回顾:
- 自连接是两侧使用同一张表的普通连接,通过别名加以区分。
- 邻接表(例如使用
manager_id的自引用外键)可以在一张表中表示树。 - 使用
INNER JOIN获取匹配的配对;使用LEFT JOIN保留父级为 NULL 的根行。 - 自连接适用于固定深度;无界遍历需要递归 CTE。
常见问题解答
「使用 SELF JOIN 处理层级关系」课时是免费的吗?
是的 — 「使用 SELF JOIN 处理层级关系」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 SQL Interview Prep 课程的其余内容,请升级到 CoddyKit PRO。 SQL Interview Prep 课程共包含 4 节课。
「使用 SELF JOIN 处理层级关系」这节课中我会学到什么?
将表连接到自身,以表示员工与经理、父级与子级之间的关系 你通过在浏览器中直接运行的动手代码来练习 SQL Interview Prep,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 SQL Interview Prep 需要有经验吗?
无需任何先前经验。CoddyKit 上的 SQL Interview Prep 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「使用 SELF JOIN 处理层级关系」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 SQL Interview Prep 课中编写并运行代码吗?
能。每节 SQL Interview Prep 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- CROSS JOIN 与笛卡尔积
- 使用 SELF JOIN 处理层级关系
- 比较同一表中的行
- 选择正确的连接类型