首页/数据/SQL 速查/自连接:表连接自身

自连接:表连接自身

同一张表连接自身:员工与经理、成对比较等场景;必须用两个不同别名区分两侧,否则无法引用。

SQL 速查数据更新于 2026-10-13

自连接是什么

自连接是把同一张表连接自身,用两个不同别名把它当成两张表来用,典型场景是员工与经理、分类与父分类、同一表内两行的成对比较。代价是中间结果接近表规模的平方,行数估算、连接键索引都要提前确认,否则一次自连接就能把库压住。

基本信息

别名两侧必须不同,否则引用歧义,MySQL 报 Not unique table/alias
连接类型保留没有上级的根节点用 LEFT JOIN,只要叶子节点用 INNER JOIN
连接键一般是主键与父级关系列,父级列务必单独建索引
层级深度只连一层用自连接,两层以上改用递归 CTE
语法形态FROM employees e JOIN employees m ON m.id = e.manager_id

员工与经理

员工与经理
SELECT e.id,
       e.name AS employee,
       m.name AS manager
FROM employees e
LEFT JOIN employees m ON m.id = e.manager_id   -- 同一张表出现两次,靠别名区分
ORDER BY e.id;
-- 去掉 LEFT 后,没有上级的 CEO 会从结果里消失

成对比较写法

成对比较写法
-- 找出金额相同的不同订单对,避免自身配对与重复配对
SELECT a.order_no AS order_a,
       b.order_no AS order_b,
       a.amount
FROM orders a
JOIN orders b ON b.amount = a.amount AND b.id > a.id  -- 用大小关系破坏对称
WHERE a.amount > 100
ORDER BY a.amount;                                    -- 漏写 b.id > a.id 会重复且含自身行

常见形态对照

形态写法要点注意事项
上下级e.manager_id = m.id根节点关系列为 NULL,必须用 LEFT JOIN
成对比较b.id > a.id 打破对称漏写会得到 (a,b) 与 (b,a) 两条并含自身
先后关系a.created_at < b.created_at时间相同时补主键做次级条件
查找孤立行LEFT JOIN 自身后判 IS NULL比 NOT EXISTS 直观,但性能通常略差
替代方案窗口函数或分组聚合多数场景只扫一遍表,代价更低

典型使用场景

  • 组织架构:一张表同时列出员工与直属上级
  • 分类树:列出子分类及其父分类名称
  • 同一表内成对或相邻比较:找差值、找重复记录
  • 查找缺失关系:找出没有下级的管理节点
  • 合并去重:按业务键同表连接自身,找出重复键的全部记录
  • 作为窗口函数替代方案的对照写法,用于验证两种实现结果是否一致
自连接不是唯一解:找“每组最新一行”“层级展开”这类需求,窗口函数或递归 CTE 通常只扫一遍表,比自连接的平方级中间结果省资源,先评估再动手。
自连接不必然扫两遍全表:连接键有索引时优化器能把内层变成等值查找;没有索引时外层每行都要扫一遍内层,大表上耗时近似按平方增长,执行前先用 EXPLAIN 看实际访问方式。

易错点

  • 两侧写法或别名相同:MySQL 报 Not unique table/alias,语句直接失败,两个别名必须不同
  • 用 INNER JOIN 做上下级展示:没有上级的根节点被整行丢掉,按业务语义选连接类型
  • 成对比较漏写不等条件:结果出现 (a,b) 与 (b,a) 两条重复记录,还包含行与自身比较
  • 无索引做自连接:内层退化成全表扫描,耗时随数据量近似平方增长,先确认连接键有索引
  • 超过两层的层级硬用自连接:别名越堆越多不可维护,应改用递归 CTE

数据来源与参考

  • MySQL 8.0 Reference Manual · Self-Joins
  • PostgreSQL Documentation · Joined Tables
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌