自连接用于查上下级关系、时间环比及重复记录,需注意别名区分角色、连接条件方向、NULL处理及性能优化。
当一张员工表里同时存着员工 ID 和其直属上级 ID,要查出“张三的直属上级是谁”或“李四的所有下属”,就必须用自连接。关键在于给同一张表起两个别名,分别代表“员工”和“上级”两个角色。
常见错误是忘记加连接条件,导致笛卡尔积;或者混淆 ON 中的字段方向,比如写成 e1.manager_id = e2.employee_id 却误以为是“e1 是下属”,实际可能反了。
SELECT e1.name AS employee, e2.name AS manager FROM employees e1 JOIN employees e2 ON e1.manager_id = e2.employee_id
manager_id 为 NULL 的 CEO 或顶层节点,外连接才能保留在结果中销售表按天记录销售额,想看“昨天比前天增长了多少”,就得把表按日期错位自连接:一张代表“今天”,一张代表“昨天”。核心是用日期运算构造匹配逻辑。
容易踩的坑是忽略时区或未处理缺失日期——比如周日没数据,那周一就找不到“周日”的记录,INNER JOIN 会直接丢掉这一行。
SELECT t1.date, t1.amount - t2.amount AS diff FROM sales t1 JOIN sales t2 ON t1.date = DATE_ADD(t2.date, INTERVAL 1 DAY)
LEFT JOIN 可保留无前一日数据的记录,差值为 
NULL
LAG()),此时自连接反而冗余且慢用户表里没有唯一约束,需要快速定位哪些 phone 出现了两次以上,自连接是最直觉的方式:让表自己跟自己配对,筛选出 phone 相同但 id 不同的组合。
但要注意性能——全表自连接是 O(n²),百万级数据可能卡死。生产环境更推荐先用 GROUP BY phone HAVING COUNT(*) > 1 找出问题号码,再关联查详情。
SELECT DISTINCT u1.phone FROM users u1 JOIN users u2 ON u1.phone = u2.phone AND u1.id (用 避免重复配对)
IN 子查询或 EXISTS,比自连接更易读也更快phone 字段建索引,否则 JOIN 会走全表扫描自连接不是炫技手段,它的存在意义在于“必须用同一张表的两份视角回答一个问题”。一旦发现需要在单表中横向比较行与行之间的关系,又无法靠聚合或窗口函数一步到位,这时候才该考虑它。别为了用而用,尤其是涉及大数据量或深层级时,先想有没有更稳更快的替代路径。