Oracle SQL 复杂连接详解
Oracle SQL 复杂连接详解
适用版本:Oracle Database 10g / 11g / 12c / 19c / 23ai 文档版本:v1.0 / 2026-07
1. 概述
复杂 JOIN 场景汇总[1]:
详细见:Oracle JOIN 连接方式。
2. JOIN 类型
2.1 INNER
SELECT e.name, d.dept_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.id;
2.2 LEFT OUTER
SELECT e.name, d.dept_name
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.id;
-- 所有员工,部门可能 NULL
2.3 RIGHT OUTER
SELECT e.name, d.dept_name
FROM employees e
RIGHT JOIN departments d ON e.dept_id = d.id;
-- 所有部门,员工可能 NULL
2.4 FULL OUTER
SELECT e.name, d.dept_name
FROM employees e
FULL JOIN departments d ON e.dept_id = d.id;
-- 所有员工 + 所有部门
2.5 CROSS
SELECT e.name, d.dept_name
FROM employees e
CROSS JOIN departments d;
-- 笛卡尔积
3. 多表 JOIN
SELECT e.name, d.dept_name, p.project_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.id
INNER JOIN emp_projects ep ON e.id = ep.emp_id
INNER JOIN projects p ON ep.project_id = p.id
WHERE e.status = 'ACTIVE';
4. 自连接
4.1 经理
SELECT e.name AS employee, m.name AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;
4.2 层级
SELECT e.name, LEVEL,
SYS_CONNECT_BY_PATH(name, '/') AS path
FROM employees e
START WITH manager_id IS NULL
CONNECT BY PRIOR id = manager_id;
详细见:Oracle 层次查询。
5. JOIN 与 USING
-- USING(列名相同)
SELECT *
FROM employees e
JOIN departments d USING (dept_id);
-- dept_id 自动去重
5.1 NATURAL
-- 自然连接(同名列自动)
SELECT *
FROM employees
NATURAL JOIN departments;
-- 不推荐,不明确
6. JOIN 算法
6.1 Nested Loop
- 小表驱动大表
- 索引利用
- OLTP
6.2 Hash Join
- 大表
- 等值
- 内存
- 仓库
6.3 Sort Merge
- 已排序
- 不等值
- 大数据
6.4 Hint
SELECT /*+ USE_NL(a b) */ ...
SELECT /*+ USE_HASH(a b) */ ...
SELECT /*+ USE_MERGE(a b) */ ...
SELECT /*+ LEADING(a b) */ ...
详细见:Oracle SQL Hint 详解。
7. OUTER JOIN 应用
7.1 找未匹配
-- 没有部门的员工
SELECT e.name
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.id
WHERE d.id IS NULL;
7.2 找孤儿
-- 没有员工的部门
SELECT d.dept_name
FROM departments d
LEFT JOIN employees e ON d.id = e.dept_id
WHERE e.id IS NULL;
7.3 比对
-- 找出表 A 有但表 B 没有
SELECT a.id
FROM table_a a
LEFT JOIN table_b b ON a.id = b.id
WHERE b.id IS NULL;
8. 子查询 JOIN
8.1 派生表
SELECT e.name, d.dept_name, s.avg_sal
FROM employees e
JOIN departments d ON e.dept_id = d.id
JOIN (SELECT dept_id, AVG(salary) AS avg_sal FROM employees GROUP BY dept_id) s
ON e.dept_id = s.dept_id
WHERE e.salary > s.avg_sal;
8.2 CTE
WITH dept_avg AS (
SELECT dept_id, AVG(salary) AS avg_sal
FROM employees
GROUP BY dept_id
)
SELECT e.name, e.salary, d.avg_sal
FROM employees e
JOIN dept_avg d ON e.dept_id = d.dept_id
WHERE e.salary > d.avg_sal;
详细见:Oracle CTE 与递归查询。
9. EXISTS
9.1 替代 JOIN
-- JOIN
SELECT DISTINCT d.*
FROM departments d
JOIN employees e ON d.id = e.dept_id;
-- EXISTS
SELECT *
FROM departments d
WHERE EXISTS (SELECT 1 FROM employees e WHERE e.dept_id = d.id);
9.2 NOT EXISTS
SELECT *
FROM departments d
WHERE NOT EXISTS (SELECT 1 FROM employees e WHERE e.dept_id = d.id);
详细见:Oracle 子查询与 EXISTS。
10. 复杂场景
10.1 多维 JOIN
SELECT
e.name,
d.dept_name,
l.location,
c.country_name
FROM employees e
JOIN departments d ON e.dept_id = d.id
JOIN locations l ON d.location_id = l.id
JOIN countries c ON l.country_id = c.country_id
WHERE c.region_id = 1;
10.2 自连接 + JOIN
SELECT e.name, m.name AS manager, d.dept_name
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id
JOIN departments d ON e.dept_id = d.id;
10.3 聚合 JOIN
SELECT d.dept_name, e.name, e.salary, s.dept_avg
FROM employees e
JOIN departments d ON e.dept_id = d.id
JOIN (
SELECT dept_id, AVG(salary) AS dept_avg
FROM employees
GROUP BY dept_id
) s ON e.dept_id = s.dept_id
WHERE e.salary > s.dept_avg;
11. 性能
11.1 JOIN 顺序
- 小表驱动大表
- 索引利用
- LEADING Hint
11.2 索引
- JOIN 列索引
- 复合索引
- 覆盖
详细见:Oracle 索引优化策略详解。
11.3 执行计划
EXPLAIN PLAN FOR ...;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY));
详细见:Oracle 执行计划详解。
12. 老式语法
12.1 避免使用
-- 老式(避免)
SELECT e.name, d.dept_name
FROM employees e, departments d
WHERE e.dept_id = d.id;
-- 外连接(避免)
SELECT e.name, d.dept_name
FROM employees e, departments d
WHERE e.dept_id = d.id(+);
12.2 推荐
-- 现代 JOIN
SELECT e.name, d.dept_name
FROM employees e
JOIN departments d ON e.dept_id = d.id;
13. 常见坑与排错
13.1 笛卡尔积
- 忘 JOIN 条件
- 性能灾难
- 检查
13.2 NULL 处理
- OUTER JOIN
- IS NULL
13.3 列歧义
-- 列名歧义
SELECT id FROM a JOIN b ON a.id = b.id; -- ERROR
-- 限定
SELECT a.id FROM a JOIN b ON a.id = b.id;
14. 最佳实践
- 现代 JOIN:清晰
- 别名:简洁
- ON 条件:明确
- OUTER 谨慎:性能
- 避免 CROSS:意外
- 索引 JOIN 列:性能
- CTE:复杂
- EXISTS:替代
- 执行计划:验证
- 测试:完整
15. 参考资料
[1] Oracle Database SQL Language Reference 19c, “Joins” https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/SELECT.html