半联结是在两个数据集(表)之间的联结,其中第一个数据集中的数据行在决定是否返回时会根据在另一个数据集中出现或不出现至少一个相匹配的数据行来确定。“不出先”匹配行——这是半联结的一种特殊形式,称为反联结。
标准的内联结与半联结之间最主要的区别在于在半联结中,第1个数据集中的每一条记录至返回一次,而不管在第二个数据集中有几条匹配的数据。这个定义表明这个查询的实际处理过程可以通过在找到第1个匹配以后马上停止第二个查询来进行优化。
从本质上来说,这就是半联结:可以通过在查询2执行完成之前停止处理该查询来进行优化。这种联结技术在oracle基于成本的优化器中查询中又包含在IN或EXISTS句中的子查询时是一种可选方案。
1、半联结
/*+ SEMIJOIN */  进行半联结(优化器选用使用哪种类型)。
/*+ NO_SEMIJOIN */  显示的意味着不行进半联结。
- SQL> select * from emp order by job;  
-   
-      EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO  
-       7788 SCOTT      ANALYST         7566 19-4月 -87           3000                    20  
-       7902 FORD       ANALYST         7566 03-12月-81           3000                    20  
-       7934 MILLER     CLERK           7782 23-1月 -82           1300                    10  
-       7900 JAMES      CLERK           7698 03-12月-81            950                    30  
-       7369 SMITH      CLERK           7902 17-12月-80            800                    20  
-       7876 ADAMS      CLERK           7788 23-5月 -87           1100                    20  
-       7698 BLAKE      MANAGER         7839 01-5月 -81           2850                    30  
-       7566 JONES      MANAGER         7839 02-4月 -81           2975                    20  
-       7782 CLARK      MANAGER         7839 09-6月 -81           2450                    10  
-       7839 KING       PRESIDENT            17-11月-81           5000                    10  
-       7844 TURNER     SALESMAN        7698 08-9月 -81           1500          0         30  
-   
-      EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO  
-       7654 MARTIN     SALESMAN        7698 28-9月 -81           1250       1400         30  
-       7521 WARD       SALESMAN        7698 22-2月 -81           1250        500         30  
-       7499 ALLEN      SALESMAN        7698 20-2月 -81           1600        300         30  
-   
- 已选择14行。  
 
分析上面的数据可以看出JOB=‘SALESMAN‘的DEPTNO有4条重复的记录,值都是30。那么如下的子查询返回的记录就有重复的记录!
- select e.ename, e.job, e.sal  
-   from emp e  
-  where e.deptno in (select d.deptno from emp d where d.job = ‘SALESMAN‘);  
-   
- select e.ename, e.job, e.sal  
-   from emp e  
-  where exists (select null  
-           from emp d  
-          where e.deptno = d.deptno  
-            and d.job = ‘SALESMAN‘);  
 
看执行计划:
- SQL> select e.ename, e.job, e.sal  
-   2    from emp e  
-   3   where e.deptno in (select d.deptno from emp d where d.job = ‘SALESMAN‘);  
-   
- ENAME      JOB              SAL  
- JAMES      CLERK            950  
- TURNER     SALESMAN        1500  
- BLAKE      MANAGER         2850  
- MARTIN     SALESMAN        1250  
- WARD       SALESMAN        1250  
- ALLEN      SALESMAN        1600  
-   
- 已选择6行。  
-   
-   
- 执行计划  
- Plan hash value: 977554918  
-   
- | Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
- |   0 | SELECT STATEMENT   |      |    14 |   448 |     7  (15)| 00:00:01 |  
- |*  1 |  HASH JOIN SEMI    |      |    14 |   448 |     7  (15)| 00:00:01 |  
- |   2 |   TABLE ACCESS FULL| EMP  |    14 |   294 |     3   (0)| 00:00:01 |  
- |*  3 |   TABLE ACCESS FULL| EMP  |     3 |    33 |     3   (0)| 00:00:01 |  
-   
- Predicate Information (identified by operation id):  
-   
-    1 - access("E"."DEPTNO"="D"."DEPTNO")  
-    3 - filter("D"."JOB"=‘SALESMAN‘)  
-   
- SQL>   
- SQL>   
- SQL> select e.ename, e.job, e.sal  
-   2    from emp e  
-   3   where exists (select null  
-   4            from emp d  
-   5           where e.deptno = d.deptno  
-   6             and d.job = ‘SALESMAN‘);  
-   
- ENAME      JOB              SAL  
- JAMES      CLERK            950  
- TURNER     SALESMAN        1500  
- BLAKE      MANAGER         2850  
- MARTIN     SALESMAN        1250  
- WARD       SALESMAN        1250  
- ALLEN      SALESMAN        1600  
-   
- 已选择6行。  
-   
-   
- 执行计划  
- Plan hash value: 977554918  
-   
- | Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
- |   0 | SELECT STATEMENT   |      |    14 |   448 |     7  (15)| 00:00:01 |  
- |*  1 |  HASH JOIN SEMI    |      |    14 |   448 |     7  (15)| 00:00:01 |  
- |   2 |   TABLE ACCESS FULL| EMP  |    14 |   294 |     3   (0)| 00:00:01 |  
- |*  3 |   TABLE ACCESS FULL| EMP  |     3 |    33 |     3   (0)| 00:00:01 |  
-   
- Predicate Information (identified by operation id):  
-   
-    1 - access("E"."DEPTNO"="D"."DEPTNO")  
-    3 - filter("D"."JOB"=‘SALESMAN‘)  
 
使用IN和EXISTS的执行计划是一致的,这一点就为了消除长期以来人们认为的使用EXISTS查询与使用IN查询的处理方法很不相同这种观点。注意执行计划中的“SEMI”就代表使用了半联结!
- SQL> select e.ename, e.job, e.sal  
-   2    from emp e  
-   3   where e.deptno in (select /*+ no_semijoin */  
-   4                       d.deptno  
-   5                        from emp d  
-   6                       where d.job = ‘SALESMAN‘);  
-   
- ENAME      JOB              SAL  
- ALLEN      SALESMAN        1600  
- WARD       SALESMAN        1250  
- MARTIN     SALESMAN        1250  
- BLAKE      MANAGER         2850  
- TURNER     SALESMAN        1500  
- JAMES      CLERK            950  
-   
- 已选择6行。  
-   
-   
- 执行计划  
- Plan hash value: 2389097100  
-   
- | Id  | Operation            | Name     | Rows  | Bytes | Cost (%CPU)| Time     |  
- |   0 | SELECT STATEMENT     |          |     9 |   306 |     8  (25)| 00:00:01 |  
- |*  1 |  HASH JOIN           |          |     9 |   306 |     8  (25)| 00:00:01 |  
- |   2 |   VIEW               | VW_NSO_1 |     2 |    26 |     4  (25)| 00:00:01 |  
- |   3 |    HASH UNIQUE       |          |     2 |    22 |     4  (25)| 00:00:01 |  
- |*  4 |     TABLE ACCESS FULL| EMP      |     3 |    33 |     3   (0)| 00:00:01 |  
- |   5 |   TABLE ACCESS FULL  | EMP      |    14 |   294 |     3   (0)| 00:00:01 |  
-   
- Predicate Information (identified by operation id):  
-   
-    1 - access("E"."DEPTNO"="DEPTNO")  
-    4 - filter("D"."JOB"=‘SALESMAN‘)  
-   
- SQL>   
- SQL>   
- SQL> select e.ename, e.job, e.sal  
-   2    from emp e  
-   3   where exists (select /*+ no_semijoin */  
-   4           null  
-   5            from emp d  
-   6           where e.deptno = d.deptno  
-   7             and d.job = ‘SALESMAN‘);  
-   
- ENAME      JOB              SAL  
- ALLEN      SALESMAN        1600  
- WARD       SALESMAN        1250  
- MARTIN     SALESMAN        1250  
- BLAKE      MANAGER         2850  
- TURNER     SALESMAN        1500  
- JAMES      CLERK            950  
-   
- 已选择6行。  
-   
-   
- 执行计划  
- Plan hash value: 2561671593  
-   
- | Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
- |   0 | SELECT STATEMENT   |      |     5 |   105 |    12   (0)| 00:00:01 |  
- |*  1 |  FILTER            |      |       |       |            |          |  
- |   2 |   TABLE ACCESS FULL| EMP  |    14 |   294 |     3   (0)| 00:00:01 |  
- |*  3 |   TABLE ACCESS FULL| EMP  |     1 |    11 |     3   (0)| 00:00:01 |  
-   
- Predicate Information (identified by operation id):  
-   
-    1 - filter( EXISTS (SELECT 0 FROM "EMP" "D" WHERE  
-               "D"."JOB"=‘SALESMAN‘ AND "D"."DEPTNO"=:B1))  
-    3 - filter("D"."JOB"=‘SALESMAN‘ AND "D"."DEPTNO"=:B1)  
 
使用NO_SEMIJOIN提示关闭了优化器使用半联结的能力。可以看见使用使用IN和EXISTS的执行计划还不一样了。
2、反联结
/*+ ANTIJOIN */  进行反联接(优化器决定具体类型)
/*+ USE_ANTI */  ANTIJOIN提示的旧版本
- SQL> select e.ename, e.job, e.sal  
-   2    from emp e  
-   3   where e.deptno not in  
-   4         (select d.deptno from emp d where d.job = ‘PRESIDENT‘);  
-   
- ENAME      JOB              SAL  
- JAMES      CLERK            950  
- TURNER     SALESMAN        1500  
- BLAKE      MANAGER         2850  
- MARTIN     SALESMAN        1250  
- WARD       SALESMAN        1250  
- ALLEN      SALESMAN        1600  
- FORD       ANALYST         3000  
- ADAMS      CLERK           1100  
- SCOTT      ANALYST         3000  
- JONES      MANAGER         2975  
- SMITH      CLERK            800  
-   
- 已选择11行。  
-   
-   
- 执行计划  
- Plan hash value: 4002838083  
-   
- | Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
- |   0 | SELECT STATEMENT   |      |     5 |   160 |     7  (15)| 00:00:01 |  
- |*  1 |  HASH JOIN ANTI NA |      |     5 |   160 |     7  (15)| 00:00:01 |  
- |   2 |   TABLE ACCESS FULL| EMP  |    14 |   294 |     3   (0)| 00:00:01 |  
- |*  3 |   TABLE ACCESS FULL| EMP  |     3 |    33 |     3   (0)| 00:00:01 |  
-   
- Predicate Information (identified by operation id):  
-   
-    1 - access("E"."DEPTNO"="D"."DEPTNO")  
-    3 - filter("D"."JOB"=‘PRESIDENT‘)  
-   
- SQL>   
- SQL>   
- SQL> select e.ename, e.job, e.sal  
-   2    from emp e  
-   3   where not exists (select null  
-   4            from emp d  
-   5           where e.deptno = d.deptno  
-   6             and d.job = ‘PRESIDENT‘);  
-   
- ENAME      JOB              SAL  
- JAMES      CLERK            950  
- TURNER     SALESMAN        1500  
- BLAKE      MANAGER         2850  
- MARTIN     SALESMAN        1250  
- WARD       SALESMAN        1250  
- ALLEN      SALESMAN        1600  
- FORD       ANALYST         3000  
- ADAMS      CLERK           1100  
- SCOTT      ANALYST         3000  
- JONES      MANAGER         2975  
- SMITH      CLERK            800  
-   
- 已选择11行。  
-   
-   
- 执行计划  
- Plan hash value: 3353202012  
-   
- | Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
- |   0 | SELECT STATEMENT   |      |     5 |   160 |     7  (15)| 00:00:01 |  
- |*  1 |  HASH JOIN ANTI    |      |     5 |   160 |     7  (15)| 00:00:01 |  
- |   2 |   TABLE ACCESS FULL| EMP  |    14 |   294 |     3   (0)| 00:00:01 |  
- |*  3 |   TABLE ACCESS FULL| EMP  |     3 |    33 |     3   (0)| 00:00:01 |  
-   
- Predicate Information (identified by operation id):  
-   
-    1 - access("E"."DEPTNO"="D"."DEPTNO")  
-    3 - filter("D"."JOB"=‘PRESIDENT‘)  
 
执行计划中的“ANTI” 就是反联结的意思,而在NOT IN的执行计划中还有“NA” 表示考虑NULL值的意思。
转载:http://blog.csdn.net/zq9017197/article/details/8439353
半联结&反联结!
原文:http://www.cnblogs.com/future2012lg/p/4095468.html