SELF JOIN 本身不支持递归,只能获取单层关系;查多层树形结构必须用 WITH RECURSIVE(MySQL 8.0+)、递归 CTE(PG/SQL Server),或固定深度的多层 LEFT JOIN(仅适用于浅层且稳定场景)。

为什么直接用 SELF JOIN 查树形结构会出错?
因为 SELF JOIN 本身不带递归能力,只做一次连接。如果你的树有 3 层(比如 CEO → 部门总监 → 经理 → 员工),用单次 JOIN 只能拉出两层关系,再多就漏数据。常见错误是写成:
SELECT a.name, b.name AS manager_name FROM employees a LEFT JOIN employees b ON a.manager_id = b.id;
这只能查直属上级,不是完整路径。真正要查“某员工的所有上级链”,得靠递归——但标准 SQL 的 SELF JOIN 不支持,必须配合数据库特有语法。
MySQL 8.0+ 怎么用 WITH RECURSIVE 替代单纯 SELF JOIN?
MySQL 8.0 开始支持 WITH RECURSIVE,这才是查树的正解。它本质是把递归逻辑“展开”成多次 SELF JOIN,但由数据库自动控制深度和终止条件。
-
WITH RECURSIVE必须定义 anchor(起点)和 recursive member(自身引用),且 recursive member 的JOIN必须指向同一个 CTE 名 - 必须有明确的终止条件,否则会报
Recursive query aborted after 1001 iterations - 层级字段(如
level)要手动累加,不能依赖外部变量
示例:查 id=5 的员工及其所有上级(从下往上):
WITH RECURSIVE org_tree AS ( SELECT id, name, manager_id, 1 AS level FROM employees WHERE id = 5 UNION ALL SELECT e.id, e.name, e.manager_id, ot.level + 1 FROM employees e INNER JOIN org_tree ot ON e.id = ot.manager_id ) SELECT * FROM org_tree;
PostgreSQL 和 SQL Server 的等价写法差异在哪?
PostgreSQL 语法几乎和 MySQL 一致,但 SQL Server 要求 RECURSIVE 关键字前加 AS,且对锚点查询限制更严:不能含聚合、GROUP BY 或子查询。另外:
- SQL Server 中,递归部分的列顺序和类型必须与锚点完全一致,否则报
Types don't match between the anchor and the recursive part - PostgreSQL 允许在递归成员中用
ORDER BY控制展开顺序,MySQL 不允许 - 三者都默认最大递归深度为 100,超限需显式设置:
MAXRECURSION 0(SQL Server)、search_path参数(PG)、cte_max_recursion_depth(MySQL 变量)
什么时候硬扛着用多层 SELF JOIN 反而更稳?
当树深度固定且很浅(≤ 3 层),比如组织架构只分“公司-部门-小组”,又不想依赖数据库版本特性时,手写三层 JOIN 可能比递归更直观、更容易加索引优化。
SELECT e1.name AS employee, e2.name AS team_lead, e3.name AS dept_head FROM employees e1 LEFT JOIN employees e2 ON e1.manager_id = e2.id LEFT JOIN employees e3 ON e2.manager_id = e3.id;
但要注意:LEFT JOIN 顺序不能颠倒,否则中间层为空会导致整行丢失;所有连接字段必须建索引(manager_id 是关键);一旦需求变成“查任意深度”,这种写法立刻不可维护。
递归的本质不是“多连几次”,而是“描述怎么一步步走”,别被 SELF JOIN 这个词带偏了方向——真正卡住人的,往往是终止条件没写对,或者忘了给递归字段加索引。

















