联接类型由联接条件的类型确定。
9.3.1内部联接
一个内连接(有时称为简单连接)是一个连接仅返回满足连接条件的行。内部联接是等联接或非等联接。
本节包含以下主题:
9.3.1.1等值连接
一个等值连接是内连接,其连接条件包含一个等号操作符。
以下示例是一个等值联接,因为联接条件仅包含一个等价运算符:
SELECT e.employee_id, e.last_name, d.department_name
FROM employees e, departments d
WHERE e.department_id=d.department_id;
在前面的查询中,联接条件为e.department_id=d.department_id。如果employees表中的行具有与departments表中的行中的值匹配的部门ID ,则数据库将返回合并结果;否则,数据库将返回合并结果。否则,数据库不会返回结果。
9.3.1.2非等值连接
一个nonequijoin是内连接,其连接条件包含一个操作符是不相等运算符。
以下查询列出了所有雇用日期发生在176号员工(job_history因为他在2007年换工作而被列出)的公司的雇员:
SELECT e.employee_id, e.first_name, e.last_name, e.hire_date
FROM employees e, job_history h
WHERE h.employee_id = 176
AND e.hire_date BETWEEN h.start_date AND h.end_date;
在前面的示例中,条件join employees和job_history不包含相等运算符,因此它是一个非准联接。非等联接相对罕见。
请注意,哈希联接至少需要部分等联接。以下SQL脚本包含一个相等联接条件(e1.empno = e2.empno)和一个nonquality条件:
SET AUTOTRACE TRACEONLY EXPLAIN
SELECT *
FROM scott.emp e1 JOIN scott.emp e2
ON ( e1.empno = e2.empno
AND e1.hiredate BETWEEN e2.hiredate-1 AND e2.hiredate+1 )
优化器为前面的查询选择一个哈希联接,如以下计划所示:
Execution Plan
----------------------------------------------------------
Plan hash value: 3638257876
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 174 | 5 (20)| 00:00:01 |
|* 1 | HASH JOIN | | 1 | 174 | 5 (20)| 00:00:01 |
| 2 | TABLE ACCESS FULL| EMP | 14 | 1218 | 2 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL| EMP | 14 | 1218 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("E1"."EMPNO"="E2"."EMPNO")
filter("E1"."HIREDATE">=INTERNAL_FUNCTION("E2"."HIREDATE")-1 AND
"E1"."HIREDATE"<=INTERNAL_FUNCTION("E2"."HIREDATE")+1)
9.3.1.3带连接 (范围)
甲带连接是一种特殊类型的nonequijoin其中在一个数据集的密钥值必须落在所述第二数据集的指定范围(“带”)内。同一张表既可以用作第一数据集,也可以用作第二数据集。
从Oracle Database 12c第2版(12.2)开始,数据库更有效地评估带联接。优化可避免不必要地扫描超出定义范围的行。
优化器使用成本估算来选择联接方法(哈希,嵌套循环或排序合并)和并行数据分配方法。在大多数情况下,优化的性能可与等联接相媲美。
下面的示例查询薪水比每位员工的薪水少$ 100到$ 100的员工。因此,带的宽度为$ 200。这些示例假定可以将每个员工的薪水与其自身进行比较。以下查询包括部分样本输出:
SELECT e1.last_name ||
' has salary between 100 less and 100 more than ' ||
e2.last_name AS "SALARY COMPARISON"
FROM employees e1,
employees e2
WHERE e1.salary
BETWEEN e2.salary - 100
AND e2.salary + 100;
SALARY COMPARISON
-------------------------------------------------------------
King has salary between 100 less and 100 more than King
Kochhar has salary between 100 less and 100 more than Kochhar
Kochhar has salary between 100 less and 100 more than De Haan
De Haan has salary between 100 less and 100 more than Kochhar
De Haan has salary between 100 less and 100 more than De Haan
Russell has salary between 100 less and 100 more than Russell
Partners has salary between 100 less and 100 more than Partners
...示例9-7不进行带联接优化的查询
如果不进行带联接优化,则数据库将使用以下查询计划:
------------------------------------------
PLAN_TABLE_OUTPUT
------------------------------------------
------------------------------------------
| Id | Operation | Name |
------------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | MERGE JOIN | |
| 2 | SORT JOIN | |
| 3 | TABLE ACCESS FULL | EMPLOYEES |
|* 4 | FILTER | |
|* 5 | SORT JOIN | |
| 6 | TABLE ACCESS FULL| EMPLOYEES |
------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - filter("E1"."SAL"<="E2"."SAL"+100)
5 - access(INTERNAL_FUNCTION("E1"."SAL")>="E2"."SAL"-100)
filter(INTERNAL_FUNCTION("E1"."SAL")>="E2"."SAL"-100)
在此计划中,步骤2对e1行源进行排序,而步骤5对e2行源进行排序。下表说明了排序后的行源。
表9-8排序的行源
| e1已排序(计划的第2步) | e2已排序(计划的第5步) |
|---|---|
| 24000(国王) | 24000(国王) |
| 17000(Kochhar) | 17000(Kochhar) |
| 17000(德哈恩) | 17000(德哈恩) |
| 14000(罗素) | 14000(罗素) |
| 13500(合作伙伴) | 13500(合作伙伴) |
连接通过迭代经过排序的输入(e1)开始,该输入是连接的左分支,对应于计划的步骤2。原始查询包含两个谓词:
e1.sal >= e2.sal–100,这是第5步过滤器e1.sal >= e2.sal+100,这是第4步过滤器
对于排序的行源的每次迭代e1,数据库将循环遍历行源e2,并针对步骤5过滤器检查每一行e1.sal >= e2.sal–100。如果该行通过了步骤5过滤器,则数据库将其发送到步骤4过滤器,然后继续e2针对步骤5过滤器测试下一行。但是,如果某行未能通过步骤5过滤器,则扫描将e2停止,并且数据库将继续进行的下一次迭代e1。
下表显示的第一次迭代e1,该迭代从24000 (King)数据集中开始e1。数据库确定的第一行e2(即)24000 (King)通过了步骤5过滤器。然后,数据库将该行发送到步骤4过滤器e1.sal <= w2.sal+100,该过滤器也通过。数据库将该行发送到MERGE行源。接下来,数据库将检查17000 (Kochhar)步骤5筛选器,该筛选器也会通过。但是,该行无法通过步骤4过滤器,并被丢弃。数据库将继续17000 (De Haan)对步骤5过滤器进行测试。
表9-9 e1的第一次迭代:SORT JOIN和FILTER分开
| 扫描e2 | 步骤5筛选(e1.sal> = e2.sal–100) | 步骤4筛选(e1.sal <= e2.sal + 100) |
|---|---|---|
| 24000(国王) | 因为24000> = 23900,所以通过。发送到第4步过滤器。 | 因为24000 <= 24100,所以通过。返回行进行合并。 |
| 17000(Kochhar) | 因为24000> = 16900,所以通过。发送到第4步过滤器。 | 失败,因为24000 <= 17100为假。丢弃行。扫描e2中的下一行。 |
| 17000(德哈恩) | 因为24000> = 16900,所以通过。发送到第4步过滤器。 | 失败,因为24000 <= 17100为假。丢弃行。扫描e2中的下一行。 |
| 14000(罗素) | 因为24000> = 13900,所以通过。发送到第4步过滤器。 | 失败,因为24000 <= 14100为假。丢弃行。扫描e2中的下一行。 |
| 13500(合作伙伴) | 通过,因为24000> =13400。发送到第4步过滤器。 | 失败,因为24000 <= 13600为false。丢弃行。扫描e2中的下一行。 |
如上表所示,每一e2行都必须通过步骤5过滤器,因为e2薪水是按降序排序的。因此,步骤5过滤器始终将行发送到步骤4过滤器。由于e2薪水是按降序排列的,因此第4步过滤器必定使以开头的每一行都失败17000 (Kochhar)。之所以会发生效率低下,是因为数据库先e2对必须通过的步骤5筛选器进行测试,然后再对必然失败的步骤4筛选器进行测试,以测试随后的所有行。
示例9-8带联接优化的查询
从Oracle Database 12c第2版(12.2)开始,数据库通过使用以下计划来优化波段联接,该计划没有单独的FILTER操作:
------------------------------------------
PLAN_TABLE_OUTPUT
------------------------------------------
| Id | Operation | Name |
------------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | MERGE JOIN | |
| 2 | SORT JOIN | |
| 3 | TABLE ACCESS FULL | EMPLOYEES |
|* 4 | SORT JOIN | |
| 5 | TABLE ACCESS FULL | EMPLOYEES |
------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access(INTERNAL_FUNCTION("E1"."SALARY")>="E2"."SALARY"-100)
filter(("E1"."SALARY"<="E2"."SALARY"+100 AND
INTERNAL_FUNCTION("E1"."SALARY")>="E2"."SALARY"-100))
不同之处在于,第4步AND对这两个谓词使用布尔逻辑来创建单个过滤器。数据库没有针对一个过滤器检查一行,然后将其发送到另一行源以针对第二个过滤器进行检查,而是对一个过滤器执行了一项检查。如果检查失败,则处理停止。
在这个例子中,查询开始的第一次迭代e1,这与开始24000 (King)。下图表示范围。e2低于23900和高于24100的值不在此范围内。
下表显示了数据库针对步骤4过滤器测试的第一行e2,即24000 (King)。该行通过测试,因此数据库发送要合并的行。下一行e2是17000 (Kochhar)。该行不在范围(带)之内,因此不满足过滤谓词,因此数据库e2在此迭代中停止测试行。数据库停止测试,因为降序排序e2确保所有后续行e2均未通过过滤器测试。因此,数据库可以进行的第二次迭代e1。
表9-10 e1的第一次迭代:单个SORT JOIN
| 扫描e2 | 过滤器4(e1.sal> = e2.sal – 100)AND(e1.sal <= e2.sal + 100) |
|---|---|
| 24000(国王) | 通过测试是因为确实是这样 将行发送到 |
| 17000(Kochhar) | 测试失败,因为它是错误的 停止扫描 |
| 17000(德哈恩) | 不适用 |
| 14000(罗素) | 不适用 |
| 13500(合作伙伴) | 不适用 |
这样,带加入优化消除了不必要的处理。e2数据库不扫描未优化情况下的每一行,而仅扫描最少的两行。




