Oracle SQL 连接方式(JOIN)全解
Oracle SQL 连接方式(JOIN)全解
适用版本:Oracle Database 9i / 10g / 11g / 12c / 19c / 23ai 文档版本:v1.0 / 2026-07
1. 概述
Oracle SQL 支持多种 JOIN 方式[1]:
| JOIN 类型 | 说明 |
|---|---|
| INNER JOIN | 内连接 |
| LEFT JOIN | 左外连接 |
| RIGHT JOIN | 右外连接 |
| FULL JOIN | 全外连接 |
| CROSS JOIN | 交叉连接 |
| NATURAL JOIN | 自然连接 |
| SELF JOIN | 自连接 |
2. INNER JOIN(内连接)
2.1 语法
-- ANSI 语法
SELECT e.id, e.name, d.dept_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.id;
-- Oracle 语法
SELECT e.id, e.name, d.dept_name
FROM employees e, departments d
WHERE e.dept_id = d.id;
2.2 结果
仅返回匹配的行。
3. LEFT OUTER JOIN(左外连接)
3.1 语法
-- ANSI 语法
SELECT e.id, e.name, d.dept_name
FROM employees e
LEFT OUTER JOIN departments d ON e.dept_id = d.id;
-- Oracle 语法
SELECT e.id, e.name, d.dept_name
FROM employees e, departments d
WHERE e.dept_id = d.id(+);
3.2 结果
返回左表所有行,右表无匹配则 NULL。
4. RIGHT OUTER JOIN(右外连接)
4.1 语法
-- ANSI 语法
SELECT e.id, e.name, d.dept_name
FROM employees e
RIGHT OUTER JOIN departments d ON e.dept_id = d.id;
-- Oracle 语法
SELECT e.id, e.name, d.dept_name
FROM employees e, departments d
WHERE e.dept_id(+) = d.id;
4.2 结果
返回右表所有行,左表无匹配则 NULL。
5. FULL OUTER JOIN(全外连接)
5.1 语法
-- ANSI 语法
SELECT e.id, e.name, d.dept_name
FROM employees e
FULL OUTER JOIN departments d ON e.dept_id = d.id;
-- Oracle 旧语法不支持,需用 UNION
SELECT e.id, e.name, d.dept_name
FROM employees e, departments d
WHERE e.dept_id = d.id(+)
UNION
SELECT e.id, e.name, d.dept_name
FROM employees e, departments d
WHERE e.dept_id(+) = d.id;
5.2 结果
返回两表所有行,无匹配则 NULL。
6. CROSS JOIN(交叉连接)
6.1 语法
-- ANSI 语法
SELECT e.name, d.dept_name
FROM employees e
CROSS JOIN departments d;
-- Oracle 语法
SELECT e.name, d.dept_name
FROM employees e, departments d;
6.2 结果
笛卡尔积,行数 = 左表行数 × 右表行数。
7. NATURAL JOIN(自然连接)
7.1 语法
SELECT id, name, dept_name
FROM employees
NATURAL JOIN departments;
7.2 结果
自动按同名列连接。
7.3 注意
- 不推荐使用
- 同名列必须类型一致
- 易出错
8. USING 子句
8.1 语法
SELECT e.id, e.name, d.dept_name
FROM employees e
JOIN departments d USING (dept_id);
8.2 说明
- 指定连接列
- 列不能加表前缀
- 列名必须一致
9. SELF JOIN(自连接)
9.1 语法
-- 员工与经理
SELECT e.name AS employee, m.name AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;
9.2 应用
- 层级关系
- 树形结构
- 同表对比
10. 多表连接
10.1 多表 INNER JOIN
SELECT e.name, d.dept_name, j.job_title
FROM employees e
INNER JOIN departments d ON e.dept_id = d.id
INNER JOIN jobs j ON e.job_id = j.id;
10.2 混合 JOIN
SELECT e.name, d.dept_name, j.job_title, l.location
FROM employees e
INNER JOIN departments d ON e.dept_id = d.id
LEFT JOIN jobs j ON e.job_id = j.id
LEFT JOIN locations l ON d.location_id = l.id;
11. JOIN 执行机制
11.1 连接算法
| 算法 | 说明 | 适用 |
|---|---|---|
| Nested Loop | 嵌套循环 | 小表驱动大表 |
| Hash Join | 哈希连接 | 大表等值连接 |
| Sort Merge | 排序合并 | 排序数据 |
| Cartesian | 笛卡尔积 | 无条件 |
11.2 Nested Loop Join
for r1 in outer_table loop
for r2 in inner_table loop
if match(r1, r2) then output
end loop
end loop
11.3 Hash Join
1. 构建阶段:扫描小表,构建 hash 表
2. 探测阶段:扫描大表,hash 查找匹配
详细内容见:Oracle 执行计划与 JOIN 算法。
12. JOIN 优化
12.1 索引使用
-- 连接列加索引
CREATE INDEX idx_emp_dept ON employees(dept_id);
CREATE INDEX idx_dept_id ON departments(id);
12.2 Hints
-- 强制使用索引
SELECT /*+ INDEX(e idx_emp_dept) */ e.name, d.dept_name
FROM employees e
JOIN departments d ON e.dept_id = d.id;
-- 强制 Hash Join
SELECT /*+ USE_HASH(e d) */ e.name, d.dept_name
FROM employees e
JOIN departments d ON e.dept_id = d.id;
-- 强制 Nested Loop
SELECT /*+ USE_NL(e d) */ e.name, d.dept_name
FROM employees e
JOIN departments d ON e.dept_id = d.id;
12.3 驱动表选择
-- 小表驱动大表
SELECT /*+ LEADING(d) USE_NL(e) */ e.name, d.dept_name
FROM employees e, departments d
WHERE e.dept_id = d.id;
13. 常见坑与排错
13.1 笛卡尔积
现象:结果行数异常多。
修复:
-- 检查 WHERE 条件
SELECT e.name, d.dept_name
FROM employees e, departments d;
-- 缺少 WHERE e.dept_id = d.id
13.2 外连接方向错误
修复:
-- (+) 在哪边,哪边可为 NULL
WHERE e.dept_id(+) = d.id -- 左外连接(员工为空)
WHERE e.dept_id = d.id(+) -- 右外连接(部门为空)
13.3 性能差
修复:
-- 1. 检查执行计划
EXPLAIN PLAN FOR SELECT ...;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
-- 2. 添加索引
-- 3. 使用 Hints
-- 4. 优化 JOIN 顺序
13.4 NULL 值处理
-- NULL 不等于 NULL
SELECT * FROM t1 JOIN t2 ON t1.id = t2.id;
-- 不会匹配 id 为 NULL 的行
-- 处理 NULL
SELECT * FROM t1 JOIN t2 ON NVL(t1.id, -1) = NVL(t2.id, -1);
14. 最佳实践
- 使用 ANSI JOIN 语法:清晰易读
- 连接列加索引:提升性能
- 小表驱动大表:减少 I/O
- **避免 SELECT ***:明确列名
- 检查执行计划:验证 JOIN 算法
- 合理使用 Hints:优化执行
- 外连接注意方向:避免错误
- NULL 处理:NVL/COALESCE
- 避免 NATURAL JOIN:易出错
- 测试大数据量:验证性能
15. 参考资料
[1] Oracle Database SQL Language Reference 19c, “SELECT” https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/SELECT.html
[2] Oracle Database Performance Tuning Guide 19c, “Joins” https://docs.oracle.com/en/database/oracle/oracle-database/19/tgsql/