self join 是通过给同一张表起不同别名实现自关联,用于查询员工与直属上级等上下级关系;必须用它是因为普通 join 无法区分同一表中的两个角色,不加别名会报错“not unique table/alias”。

什么是 SELF JOIN,为什么必须用它查上下级
SELF JOIN 是 MySQL 中对同一张表做两次引用的写法,不是语法关键字,而是通过 AS 给表起别名实现的。员工表里通常用 id 和 manager_id 字段表示上下级关系,比如 manager_id = 101 意味着该员工直属上级是 id 为 101 的人。这种“自己连自己”的结构没法用普通 JOIN 解决——你不能用 employees JOIN employees,必须区分“员工”和“上级”两个角色。
常见错误现象:ERROR 1066: Not unique table/alias: 'employees',就是没起别名导致的。
- 必须给同一张表起两个不同别名,比如
employees e(当前员工)和employees m(其上级) - JOIN 条件要明确指向关系字段:
e.manager_id = m.id,而不是反过来 - 如果想查某人所有下级(不止直接下属),SELF JOIN 不够用,得配合递归 CTE(MySQL 8.0+)或应用层循环
查直接上级:最简 SELF JOIN 示例
这是最常用场景:给定一个员工 ID,查他直属领导的姓名、部门等信息。
SELECT e.name AS employee_name, m.name AS manager_name, m.position AS manager_position FROM employees e LEFT JOIN employees m ON e.manager_id = m.id WHERE e.id = 123;
注意点:
- 用
LEFT JOIN而非INNER JOIN,否则 CEO(manager_id为 NULL)会查不到结果 -
WHERE放在 JOIN 之后,过滤的是“员工”记录;若把条件写进ON,逻辑会变(比如ON e.manager_id = m.id AND m.active = 1会影响 LEFT JOIN 的 NULL 补全行为) - 字段别名(
AS employee_name)不是可选的——不加会导致两个name冲突报错
查所有直接下属:反向关联的写法
和上一个方向相反:给定一个经理 ID,列出他带的所有一级下属。
SELECT m.name AS manager_name, e.name AS reportee_name, e.hire_date FROM employees m INNER JOIN employees e ON e.manager_id = m.id WHERE m.id = 101;
关键差异:
- 这里用
INNER JOIN更合理——如果某人不是任何人的manager_id,就不该出现在结果里 - JOIN 条件仍是
e.manager_id = m.id,但主表换成了经理(m),所以 WHERE 过滤的是经理 ID - 如果经理没有下属,整行不会返回(
INNER JOIN特性),这和上例的LEFT JOIN行为形成对比
查完整上下级链(如:员工 → 经理 → 总监 → CEO)
MySQL 8.0+ 支持递归 CTE,这是唯一能一步查出多层关系的方式。SELF JOIN 只能写死层数(比如连 3 次表查到总监),既难维护又无法应对深度不确定的组织结构。
WITH RECURSIVE org_tree AS ( SELECT id, name, manager_id, 1 AS level FROM employees WHERE id = 123 -- 起始员工 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;
容易被忽略的细节:
- 递归 CTE 必须包含两部分:非递归种子(
WHERE id = 123)和递归成员(INNER JOIN org_tree),缺一不可 - 递归方向是“向上找上级”,所以第二部分是
e.id = ot.manager_id(当前层级的 manager_id,等于上一层的 id) - 如果没有终止条件(比如 manager_id 为 NULL),可能无限循环——MySQL 默认有
cte_max_recursion_depth限制(默认 1000),但最好在业务逻辑中显式判断manager_id IS NOT NULL
实际用的时候,别硬套 SELF JOIN 去模拟多层关系。该用递归就用递归,该分步查就分步查。组织架构深度一旦超过 3 层,硬写多个 SELF JOIN 不仅慢,而且改个字段名就得同步改七八处。











