一个外部联接返回满足连接条件,并且还从一个表的行对于没有从另一表中的行满足条件的所有行。因此,外部联接的结果集是内部联接的超集。
在ANSI语法中,该OUTER JOIN子句指定外部联接。在FROM子句中,左表显示在OUTER JOIN关键字的左侧,右表显示在这些关键字的右侧。左表也称为外部表,右表也称为内部表。例如,在以下语句中,employees表是左侧表或外部表:
SELECT employee_id, last_name, first_name
FROM employees LEFT OUTER JOIN departments
ON (employees.department_id=departments.departments_id);
外部联接要求将外部联接的表作为驱动表。在前面的示例中,employees是驱动表,并且departments是被驱动表。
- 嵌套循环外部联接 : 数据库使用此操作在两个表之间循环通过外部联接。外部联接返回外部(保留)表行,即使内部(可选)表中没有相应的行也是如此。
- 哈希联接外部联接 : 当数据量足够大以提高哈希联接的效率,或者无法从外部表驱动到内部表时,优化器将使用哈希联接来处理外部联接。
- 对合并的外部联接进行排序 : 当外部联接无法从外部(保留)表驱动到内部(可选)表时,它不能使用哈希联接或嵌套循环联接。
- 全外连接 : 一个完全外部连接是左,右外连接的组合。
- 外部联接左侧的多个表 : 在Oracle Database 12c中,外部联接表的左侧可能存在多个表。
9.3.2.1嵌套循环外部联接
数据库使用此操作在两个表之间循环进行外部联接。外部联接返回外部(保留)表行,即使内部(可选)表中没有相应的行也是如此。
在标准的嵌套循环中,优化器根据成本选择表的顺序(驱动表和从动表)。但是,在嵌套循环外部联接中,联接条件确定表的顺序。数据库使用外部行保留表驱动到内部表。
在以下情况下,优化器使用嵌套循环联接来处理外部联接:
- 可以从外表驱动到内表。
- 数据量不足以使嵌套循环方法高效。
对于嵌套循环外部联接的示例,可以在示例9-9中添加USE_NL提示,以指示优化器使用嵌套循环。例如:
SELECT /*+ USE_NL(c o) */ cust_last_name,
SUM(NVL2(o.customer_id,0,1)) "Count"
FROM customers c, orders o
WHERE c.credit_limit > 1000
AND c.customer_id = o.customer_id(+)
GROUP BY cust_last_name;9.3.2.2哈希联接外部联接
当数据量足够大以提高哈希联接的效率,或者无法从外部表驱动到内部表时,优化器将使用哈希联接来处理外部联接。
成本决定表格的顺序。外部表(包括保留的行)可以用于构建哈希表,也可以用于探测哈希表。
示例9-9哈希联接外部联接
此示例显示了典型的哈希联接外部联接查询及其执行计划。在此示例中,将查询所有信用额度大于1000的客户。需要外部联接,以便查询捕获没有订单的客户。
- 外面的桌子是
customers。 - 内部表是
orders。 - 联接保留
customers行,包括中没有对应行的行orders。
您可以使用NOT EXISTS子查询返回行。但是,因为查询的是表中的所有行,所以哈希联接的性能更好(除非NOT EXISTS不嵌套子查询)。
SELECT cust_last_name, SUM(NVL2(o.customer_id,0,1)) "Count"
FROM customers c, orders o
WHERE c.credit_limit > 1000
AND c.customer_id = o.customer_id(+)
GROUP BY cust_last_name;
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 7 (100)| |
| 1 | HASH GROUP BY | | 168 | 3192 | 7 (29)| 00:00:01 |
|* 2 | HASH JOIN OUTER | | 318 | 6042 | 6 (17)| 00:00:01 |
|* 3 | TABLE ACCESS FULL| CUSTOMERS | 260 | 3900 | 3 (0)| 00:00:01 |
|* 4 | TABLE ACCESS FULL| ORDERS | 105 | 420 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("C"."CUSTOMER_ID"="O"."CUSTOMER_ID")
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------
3 - filter("C"."CREDIT_LIMIT">1000)
4 - filter("O"."CUSTOMER_ID">0)该查询查找满足各种条件的客户。当外部联接NULL在内部表中找不到任何对应的行时,它将返回内部表的列以及外部(保留的)表行。此操作将查找customers没有任何orders行的所有行。
在这种情况下,外部联接条件如下:
customers.customer_id = orders.customer_id(+)
此条件的组件表示以下内容:
示例9-10外部联接到多表视图
在此示例中,外部联接是多表视图。优化器无法像普通联接一样进入视图或推送谓词,因此它会构建视图的整个行集。
SELECT c.cust_last_name, sum(revenue)
FROM customers c, v_orders o
WHERE c.credit_limit > 2000
AND o.customer_id(+) = c.customer_id
GROUP BY c.cust_last_name;
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 144 | 4608 | 16 (32)|
| 1 | HASH GROUP BY | | 144 | 4608 | 16 (32)|
|* 2 | HASH JOIN OUTER | | 663 | 21216 | 15 (27)|
|* 3 | TABLE ACCESS FULL | CUSTOMERS | 195 | 2925 | 6 (17)|
| 4 | VIEW | V_ORDERS | 665 | 11305 | |
| 5 | HASH GROUP BY | | 665 | 15960 | 9 (34)|
|* 6 | HASH JOIN | | 665 | 15960 | 8 (25)|
|* 7 | TABLE ACCESS FULL| ORDERS | 105 | 840 | 4 (25)|
| 8 | TABLE ACCESS FULL| ORDER_ITEMS | 665 | 10640 | 4 (25)|
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("O"."CUSTOMER_ID"(+)="C"."CUSTOMER_ID")
3 - filter("C"."CREDIT_LIMIT">2000)
6 - access("O"."ORDER_ID"="L"."ORDER_ID")
7 - filter("O"."CUSTOMER_ID">0)
视图定义如下:
CREATE OR REPLACE view v_orders AS
SELECT l.product_id, SUM(l.quantity*unit_price) revenue,
o.order_id, o.customer_id
FROM orders o, order_items l
WHERE o.order_id = l.order_id
GROUP BY l.product_id, o.order_id, o.customer_id;
9.3.2.3排序合并外部联接
当外部联接无法从外部(保留)表驱动到内部(可选)表时,它不能使用哈希联接或嵌套循环联接。
在这种情况下,它使用排序合并外部联接。
在以下情况下,优化器对外部联接使用排序合并:
- 嵌套循环联接效率很低。由于数据量大,嵌套循环联接可能效率低下。
- 优化程序发现,由于其他操作需要排序,因此在散列连接上使用排序合并会更便宜。
9.3.2.4完全外部联接
一个完全外部连接是左和右外的组合连接。
除了内部联接之外,两个表中由于内部联接而未返回的行也将保留并扩展为null。换句话说,完全外部联接将表联接在一起,但显示联接表中没有对应行的行。
示例9-11完全外部联接
以下查询检索所有部门以及每个部门中的所有员工,但还包括:
- 任何没有部门的员工
- 任何没有员工的部门
SELECT d.department_id, e.employee_id
FROM employees e FULL OUTER JOIN departments d
ON e.department_id = d.department_id
ORDER BY d.department_id;
该语句产生以下输出:
DEPARTMENT_ID EMPLOYEE_ID
------------- -----------
10 200
20 201
20 202
30 114
30 115
30 116
...
270
280
178
207
125 rows selected.
示例9-12完整外部联接的执行计划
从Oracle Database 11 g开始,Oracle数据库会自动使用基于哈希联接的本机执行方法来尽可能执行完全外部联接。当数据库使用new方法执行完全外部联接时,查询的执行计划将包含HASH JOIN FULL OUTER。示例9-11中的查询使用以下执行计划:
--------------------------------------------------------------------------------
| Id| Operation | Name |Rows |Bytes |Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 122 | 4758 | 6 (34)| 00:0 0:01 |
| 1 | SORT ORDER BY | | 122 | 4758 | 6 (34)| 00:0 0:01 |
| 2 | VIEW | VW_FOJ_0 | 122 | 4758 | 5 (20)| 00:0 0:01 |
|*3 | HASH JOIN FULL OUTER | | 122 | 1342 | 5 (20)| 00:0 0:01 |
| 4 | INDEX FAST FULL SCAN| DEPT_ID_PK | 27 | 108 | 2 (0)| 00:0 0:01 |
| 5 | TABLE ACCESS FULL | EMPLOYEES | 107 | 749 | 2 (0)| 00:0 0:01 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("E"."DEPARTMENT_ID"="D"."DEPARTMENT_ID")
HASH JOIN FULL OUTER包含在前面的计划(步骤3)中,指示查询使用哈希完整外部联接执行方法。通常,当两个表之间的完全外部联接条件为等联接时,可以使用哈希完全外部联接执行方法,并且Oracle数据库会自动使用它。
要指示优化器考虑使用哈希完全外部联接执行方法,请应用NATIVE_FULL_OUTER_JOIN提示。要指示优化器不要考虑使用哈希完全外部联接执行方法,请应用NO_NATIVE_FULL_OUTER_JOIN提示。所述NO_NATIVE_FULL_OUTER_JOIN提示指示优化接合每个指定表时排除天然执行方法。相反,完整的外部联接将作为左外部联接和反联接的并集执行。
9.3.2.5外部联接左侧的多个表
在Oracle Database 12c中,外部联接表的左侧可能存在多个表。
此增强功能使Oracle数据库可以合并包含多个表的视图,该视图显示在外部联接的左侧。在Oracle Database 12c之前的版本中,诸如以下的查询无效,并且会触发ORA-01417错误消息:
SELECT t1.d, t3.c
FROM t1, t2, t3
WHERE t1.z = t2.z
AND t1.x = t3.x (+)
AND t2.y = t3.y (+);
从Oracle Database 12c开始,上述查询有效。




