自连接(Self Join)本质上是对同一张表按不同条件进行多次访问,在大数据量下很容易成为性能瓶颈。下面从原理 → 常见瓶颈 → 优化手段 → 示例系统性说明。
能不自连接,就不要自连接
很多自连接场景,其实可以用:
自连接字段必须建索引:
CREATE INDEX idx_emp_manager ON employee(manager_id);
CREATE INDEX idx_emp_dept ON employee(dept_id);
✅ 适合:
❌ 错误写法:
SELECT *
FROM emp a
JOIN emp b ON a.manager_id = b.id;
✅ 优化写法:
SELECT *
FROM emp a
JOIN emp b ON a.manager_id = b.id
WHERE a.dept_id = 10
AND b.status = 'ACTIVE';
先过滤,再连接
❌ 自连接:
SELECT a.*
FROM emp a
JOIN emp b
ON a.dept_id = b.dept_id
GROUP BY a.id
HAVING a.salary = MAX(b.salary);
✅ 窗口函数:
SELECT *
FROM (
SELECT *,
RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) rn
FROM emp
) t
WHERE rn = 1;
✅ 只扫描一次表
❌ 自连接:
SELECT a.*, b.value
FROM logs a
JOIN logs b
ON a.user_id = b.user_id
AND a.id = b.id + 1;
✅ LAG:
SELECT *,
LAG(value) OVER (PARTITION BY user_id ORDER BY id)
FROM logs;
WITH managers AS (
SELECT id, name
FROM emp
WHERE is_manager = 1
)
SELECT e.*, m.name
FROM emp e
JOIN managers m ON e.manager_id = m.id;
✅ 小表驱动大表
如果是组织架构 / 评论树:
CONNECT BY(Oracle)WITH RECURSIVE(MySQL / PG)WITH RECURSIVE cte AS (
SELECT * FROM emp WHERE id = 1
UNION ALL
SELECT e.* FROM emp e JOIN cte ON e.manager_id = cte.id
)
SELECT * FROM cte;
如果自连接非常频繁:
root_idlevelpath(如 /1/3/7)自连接优化 = 少扫表 + 用索引 + 换写法(窗口 / 递归 / 冗余)
如果你有具体 SQL 或业务场景(如组织架构、日志对比、树结构),我可以直接帮你重写并对比执行计划。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。