半联结是在两个数据集(表)之间的联结,其中第一个数据集中的数据行在决定是否返回时会根据在另一个数据集中出现或不出现至少一个相匹配的数据行来确定。“不出先”匹配行——这是半联结的一种特殊形式,称为反联结。

标准的内联结与半联结之间最主要的区别在于在半联结中,第1个数据集中的每一条记录至返回一次,而不管在第二个数据集中有几条匹配的数据。这个定义表明这个查询的实际处理过程可以通过在找到第1个匹配以后马上停止第二个查询来进行优化。

从本质上来说,这就是半联结:可以通过在查询2执行完成之前停止处理该查询来进行优化。这种联结技术在oracle基于成本的优化器中查询中又包含在IN或EXISTS句中的子查询时是一种可选方案。

1、半联结

/*+ SEMIJOIN */  进行半联结(优化器选用使用哪种类型)。
/*+ NO_SEMIJOIN */  显示的意味着不行进半联结。

  1. SQL> select * from emp order by job;
  2. EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO
  3. ---------- ---------- --------- ---------- -------------- ---------- ---------- ----------
  4. 7788 SCOTT      ANALYST         7566 19-4月 -87           3000                    20
  5. 7902 FORD       ANALYST         7566 03-12月-81           3000                    20
  6. 7934 MILLER     CLERK           7782 23-1月 -82           1300                    10
  7. 7900 JAMES      CLERK           7698 03-12月-81            950                    30
  8. 7369 SMITH      CLERK           7902 17-12月-80            800                    20
  9. 7876 ADAMS      CLERK           7788 23-5月 -87           1100                    20
  10. 7698 BLAKE      MANAGER         7839 01-5月 -81           2850                    30
  11. 7566 JONES      MANAGER         7839 02-4月 -81           2975                    20
  12. 7782 CLARK      MANAGER         7839 09-6月 -81           2450                    10
  13. 7839 KING       PRESIDENT            17-11月-81           5000                    10
  14. 7844 TURNER     SALESMAN        7698 08-9月 -81           1500          0         30
  15. EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO
  16. ---------- ---------- --------- ---------- -------------- ---------- ---------- ----------
  17. 7654 MARTIN     SALESMAN        7698 28-9月 -81           1250       1400         30
  18. 7521 WARD       SALESMAN        7698 22-2月 -81           1250        500         30
  19. 7499 ALLEN      SALESMAN        7698 20-2月 -81           1600        300         30
  20. 已选择14行。

分析上面的数据可以看出JOB='SALESMAN'的DEPTNO有4条重复的记录,值都是30。那么如下的子查询返回的记录就有重复的记录!

  1. 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');
  4. select e.ename, e.job, e.sal
  5. from emp e
  6. where exists (select null
  7. from emp d
  8. where e.deptno = d.deptno
  9. and d.job = 'SALESMAN');

看执行计划:

  1. SQL> select e.ename, e.job, e.sal
  2. 2    from emp e
  3. 3   where e.deptno in (select d.deptno from emp d where d.job = 'SALESMAN');
  4. ENAME      JOB              SAL
  5. ---------- --------- ----------
  6. JAMES      CLERK            950
  7. TURNER     SALESMAN        1500
  8. BLAKE      MANAGER         2850
  9. MARTIN     SALESMAN        1250
  10. WARD       SALESMAN        1250
  11. ALLEN      SALESMAN        1600
  12. 已选择6行。
  13. 执行计划
  14. ----------------------------------------------------------
  15. Plan hash value: 977554918
  16. ---------------------------------------------------------------------------
  17. | Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
  18. ---------------------------------------------------------------------------
  19. |   0 | SELECT STATEMENT   |      |    14 |   448 |     7  (15)| 00:00:01 |
  20. |*  1 |  HASH JOIN SEMI    |      |    14 |   448 |     7  (15)| 00:00:01 |
  21. |   2 |   TABLE ACCESS FULL| EMP  |    14 |   294 |     3   (0)| 00:00:01 |
  22. |*  3 |   TABLE ACCESS FULL| EMP  |     3 |    33 |     3   (0)| 00:00:01 |
  23. ---------------------------------------------------------------------------
  24. Predicate Information (identified by operation id):
  25. ---------------------------------------------------
  26. 1 - access("E"."DEPTNO"="D"."DEPTNO")
  27. 3 - filter("D"."JOB"='SALESMAN')
  28. SQL>
  29. SQL>
  30. SQL> select e.ename, e.job, e.sal
  31. 2    from emp e
  32. 3   where exists (select null
  33. 4            from emp d
  34. 5           where e.deptno = d.deptno
  35. 6             and d.job = 'SALESMAN');
  36. ENAME      JOB              SAL
  37. ---------- --------- ----------
  38. JAMES      CLERK            950
  39. TURNER     SALESMAN        1500
  40. BLAKE      MANAGER         2850
  41. MARTIN     SALESMAN        1250
  42. WARD       SALESMAN        1250
  43. ALLEN      SALESMAN        1600
  44. 已选择6行。
  45. 执行计划
  46. ----------------------------------------------------------
  47. Plan hash value: 977554918
  48. ---------------------------------------------------------------------------
  49. | Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
  50. ---------------------------------------------------------------------------
  51. |   0 | SELECT STATEMENT   |      |    14 |   448 |     7  (15)| 00:00:01 |
  52. |*  1 |  HASH JOIN SEMI    |      |    14 |   448 |     7  (15)| 00:00:01 |
  53. |   2 |   TABLE ACCESS FULL| EMP  |    14 |   294 |     3   (0)| 00:00:01 |
  54. |*  3 |   TABLE ACCESS FULL| EMP  |     3 |    33 |     3   (0)| 00:00:01 |
  55. ---------------------------------------------------------------------------
  56. Predicate Information (identified by operation id):
  57. ---------------------------------------------------
  58. 1 - access("E"."DEPTNO"="D"."DEPTNO")
  59. 3 - filter("D"."JOB"='SALESMAN')

使用IN和EXISTS的执行计划是一致的,这一点就为了消除长期以来人们认为的使用EXISTS查询与使用IN查询的处理方法很不相同这种观点。注意执行计划中的“SEMI”就代表使用了半联结!

  1. SQL> select e.ename, e.job, e.sal
  2. 2    from emp e
  3. 3   where e.deptno in (select /*+ no_semijoin */
  4. 4                       d.deptno
  5. 5                        from emp d
  6. 6                       where d.job = 'SALESMAN');
  7. ENAME      JOB              SAL
  8. ---------- --------- ----------
  9. ALLEN      SALESMAN        1600
  10. WARD       SALESMAN        1250
  11. MARTIN     SALESMAN        1250
  12. BLAKE      MANAGER         2850
  13. TURNER     SALESMAN        1500
  14. JAMES      CLERK            950
  15. 已选择6行。
  16. 执行计划
  17. ----------------------------------------------------------
  18. Plan hash value: 2389097100
  19. ---------------------------------------------------------------------------------
  20. | Id  | Operation            | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
  21. ---------------------------------------------------------------------------------
  22. |   0 | SELECT STATEMENT     |          |     9 |   306 |     8  (25)| 00:00:01 |
  23. |*  1 |  HASH JOIN           |          |     9 |   306 |     8  (25)| 00:00:01 |
  24. |   2 |   VIEW               | VW_NSO_1 |     2 |    26 |     4  (25)| 00:00:01 |
  25. |   3 |    HASH UNIQUE       |          |     2 |    22 |     4  (25)| 00:00:01 |
  26. |*  4 |     TABLE ACCESS FULL| EMP      |     3 |    33 |     3   (0)| 00:00:01 |
  27. |   5 |   TABLE ACCESS FULL  | EMP      |    14 |   294 |     3   (0)| 00:00:01 |
  28. ---------------------------------------------------------------------------------
  29. Predicate Information (identified by operation id):
  30. ---------------------------------------------------
  31. 1 - access("E"."DEPTNO"="DEPTNO")
  32. 4 - filter("D"."JOB"='SALESMAN')
  33. SQL>
  34. SQL>
  35. SQL> select e.ename, e.job, e.sal
  36. 2    from emp e
  37. 3   where exists (select /*+ no_semijoin */
  38. 4           null
  39. 5            from emp d
  40. 6           where e.deptno = d.deptno
  41. 7             and d.job = 'SALESMAN');
  42. ENAME      JOB              SAL
  43. ---------- --------- ----------
  44. ALLEN      SALESMAN        1600
  45. WARD       SALESMAN        1250
  46. MARTIN     SALESMAN        1250
  47. BLAKE      MANAGER         2850
  48. TURNER     SALESMAN        1500
  49. JAMES      CLERK            950
  50. 已选择6行。
  51. 执行计划
  52. ----------------------------------------------------------
  53. Plan hash value: 2561671593
  54. ---------------------------------------------------------------------------
  55. | Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
  56. ---------------------------------------------------------------------------
  57. |   0 | SELECT STATEMENT   |      |     5 |   105 |    12   (0)| 00:00:01 |
  58. |*  1 |  FILTER            |      |       |       |            |          |
  59. |   2 |   TABLE ACCESS FULL| EMP  |    14 |   294 |     3   (0)| 00:00:01 |
  60. |*  3 |   TABLE ACCESS FULL| EMP  |     1 |    11 |     3   (0)| 00:00:01 |
  61. ---------------------------------------------------------------------------
  62. Predicate Information (identified by operation id):
  63. ---------------------------------------------------
  64. 1 - filter( EXISTS (SELECT 0 FROM "EMP" "D" WHERE
  65. "D"."JOB"='SALESMAN' AND "D"."DEPTNO"=:B1))
  66. 3 - filter("D"."JOB"='SALESMAN' AND "D"."DEPTNO"=:B1)

使用NO_SEMIJOIN提示关闭了优化器使用半联结的能力。可以看见使用使用IN和EXISTS的执行计划还不一样了。

2、反联结

/*+ ANTIJOIN */  进行反联接(优化器决定具体类型)
/*+ USE_ANTI */  ANTIJOIN提示的旧版本

  1. SQL> select e.ename, e.job, e.sal
  2. 2    from emp e
  3. 3   where e.deptno not in
  4. 4         (select d.deptno from emp d where d.job = 'PRESIDENT');
  5. ENAME      JOB              SAL
  6. ---------- --------- ----------
  7. JAMES      CLERK            950
  8. TURNER     SALESMAN        1500
  9. BLAKE      MANAGER         2850
  10. MARTIN     SALESMAN        1250
  11. WARD       SALESMAN        1250
  12. ALLEN      SALESMAN        1600
  13. FORD       ANALYST         3000
  14. ADAMS      CLERK           1100
  15. SCOTT      ANALYST         3000
  16. JONES      MANAGER         2975
  17. SMITH      CLERK            800
  18. 已选择11行。
  19. 执行计划
  20. ----------------------------------------------------------
  21. Plan hash value: 4002838083
  22. ---------------------------------------------------------------------------
  23. | Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
  24. ---------------------------------------------------------------------------
  25. |   0 | SELECT STATEMENT   |      |     5 |   160 |     7  (15)| 00:00:01 |
  26. |*  1 |  HASH JOIN ANTI NA |      |     5 |   160 |     7  (15)| 00:00:01 |
  27. |   2 |   TABLE ACCESS FULL| EMP  |    14 |   294 |     3   (0)| 00:00:01 |
  28. |*  3 |   TABLE ACCESS FULL| EMP  |     3 |    33 |     3   (0)| 00:00:01 |
  29. ---------------------------------------------------------------------------
  30. Predicate Information (identified by operation id):
  31. ---------------------------------------------------
  32. 1 - access("E"."DEPTNO"="D"."DEPTNO")
  33. 3 - filter("D"."JOB"='PRESIDENT')
  34. SQL>
  35. SQL>
  36. SQL> select e.ename, e.job, e.sal
  37. 2    from emp e
  38. 3   where not exists (select null
  39. 4            from emp d
  40. 5           where e.deptno = d.deptno
  41. 6             and d.job = 'PRESIDENT');
  42. ENAME      JOB              SAL
  43. ---------- --------- ----------
  44. JAMES      CLERK            950
  45. TURNER     SALESMAN        1500
  46. BLAKE      MANAGER         2850
  47. MARTIN     SALESMAN        1250
  48. WARD       SALESMAN        1250
  49. ALLEN      SALESMAN        1600
  50. FORD       ANALYST         3000
  51. ADAMS      CLERK           1100
  52. SCOTT      ANALYST         3000
  53. JONES      MANAGER         2975
  54. SMITH      CLERK            800
  55. 已选择11行。
  56. 执行计划
  57. ----------------------------------------------------------
  58. Plan hash value: 3353202012
  59. ---------------------------------------------------------------------------
  60. | Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
  61. ---------------------------------------------------------------------------
  62. |   0 | SELECT STATEMENT   |      |     5 |   160 |     7  (15)| 00:00:01 |
  63. |*  1 |  HASH JOIN ANTI    |      |     5 |   160 |     7  (15)| 00:00:01 |
  64. |   2 |   TABLE ACCESS FULL| EMP  |    14 |   294 |     3   (0)| 00:00:01 |
  65. |*  3 |   TABLE ACCESS FULL| EMP  |     3 |    33 |     3   (0)| 00:00:01 |
  66. ---------------------------------------------------------------------------
  67. Predicate Information (identified by operation id):
  68. ---------------------------------------------------
  69. 1 - access("E"."DEPTNO"="D"."DEPTNO")
  70. 3 - filter("D"."JOB"='PRESIDENT')

执行计划中的“ANTI” 就是反联结的意思,而在NOT IN的执行计划中还有“NA” 表示考虑NULL值的意思。

转载:http://blog.csdn.net/zq9017197/article/details/8439353

半联结&反联结!的更多相关文章

  1. 11 半联结 & 反联结

    半联结 和 反联结是 oracle 优化器能够选择用来在获取信息时应用的两个密切相关的联结方法(实际上是联结方法的选项) 半联结 IN 的半联结 select /* using in */ depar ...

  2. 读书笔记--SQL必知必会13--创建高级联结

    13.1 使用表别名 SQL可以对列名.计算字段和表名起别名. 缩短SQL语句 允许在一条SELECT语句中多次使用相同的表. 注意:表别名只在查询执行中使用,不返回到客户端. MariaDB [sq ...

  3. SQL学习之高级联结(自联结、自然联结、外联接)

    create table Customers( Id ,), Company ) null, Name ) null ) insert into Customers values('Fun4All', ...

  4. SQL 必知必会·笔记<11>创建高级联结

    1. 使用表别名 SQL 除了可以对列名和计算字段使用别名,还允许给表名起别名.这样 做有两个主要理由: 缩短SQL 语句: 允许在一条SELECT 语句中多次使用相同的表. 使用表别名示例: SEL ...

  5. SQL 必知必会·笔记<10>联结表

    可伸缩(scale) 能够适应不断增加的工作量而不失败.设计良好的数据库或应用程序 称为可伸缩性好(scale well). 联结(JOIN) 联结(JOIN)是一种机制,用来在一条SELECT 语句 ...

  6. MySQL(七)联结表

    一.联结表基础知识 1.关系表 把信息分解成多个表,一类数据一个表,各表通过某些常用值(即关系设计中的关系(relational))互相关联: 2.外键(foreign key):外键为某个表中的一列 ...

  7. MySQL自学笔记_联结(join)

    1.  背景及原因 关系型数据库的一个基本原则是将不同细分数据放在单独的表中存储.这样做的好处是: 1).避免重复数据的出现 2).方便数据更新 3).避免创建重复数据时出错 例子: 有供应商信息和产 ...

  8. 12.创建高级联结---SQL

    一.使用表别名 SQL除了可以对列名和计算字段使用别名,还允许给表名起别名.这样做有两个主要理由: 缩短SQL语句: 允许在一条SELECT语句中多次使用相同的表. SELECT cust_name, ...

  9. mysql必知必会-创建高级联结

    使用表别名 使用别名引用被检索的表列 别名除了用于列名和计算字段外,SQL还允许给表名起别名.这样做 有两个主要理由: 缩短SQL语句: 允许在单条 SELECT 语句中多次使用相同的表. 可以看到, ...

随机推荐

  1. 【微信小程序推广营销】教你微信小程序SEO优化,让你的小程序快人一步抢占先机

    今年一月份上线的小程序,经过近一年的沉淀发酵,现在也进入了快速发展期. 在未来肯定会有越来越多的小程序诞生,小程序多了就需要搜索,那么如何让自己的小程序在众多的小程序中脱颖而出,这就需要小程序SEO优 ...

  2. ubuntu18.04 server配置静态ip (转载)

    原文地址: https://blog.csdn.net/mossan/article/details/80381679 最新发布的ubuntu18.04 server,启用了新的网络工具netplan ...

  3. php中弱语言类型的底层实现

    PHP是弱语言类型,主要分为三类: 1.标量类型:integer.string.float.boolean 2.复合类型:array.object 3.特殊类型:resource.null php是通 ...

  4. PS如何批量处理图片

    喜爱摄影的朋友可能都有这样的体会,相机里面存了大量的图片,一般都是2048×1536或者更大像素的照片,每张都有1M以上,如果设置的清晰度高,则照片就更大,这样的图片是无法上传到博客中的(博客要求每张 ...

  5. python海龟绘图

    最近学了python,看了几本书之后,才明白python的强大,python是一种解释型的语言,即每写一行程序就执行一行. 而且在科学计算方面,处理的能力特别的方便. 比如python中的字典dict ...

  6. iOS7,iOS8和iOS9的区别

    iOS7,iOS8和iOS9的区别:iOS7.0 1.iOS 7是iOS面世以来在用户界面上做出改变最大的一个操作系统.iOS 7抛弃了以往的拟物化设计,而采用了扁平化设计. 苹果在重新思考 iOS ...

  7. python 判断 txt 编码方式

    import chardet f = open('/path/file.txt',r) data = f.read() print(chardet.detect(data)

  8. day8 python学习 集合 深浅拷贝

    1.内存地址: 字符串在20位以内,没有空格,没有特殊字符的情况下,同样的字符串内存地址是一样的 2.元组中:在只有一个值的时在后边加逗号和没有逗号的区别 t1=(1) 不加逗号这个值是什么类型就打印 ...

  9. stenciljs 学习十一 pwa 支持

    stenciljs 对于pwa 的支持是自动注入的,我们只需要简单的配置,stenciljs使用workbox 配置 默认配置 { skipWaiting: true, clientsClaim: t ...

  10. UltraEdit常用设置及快捷键

    = 关闭自动加载上次文件的方法,操作方法如下:首先,要打开UltraEdit,然后点击经[高级]-[配置],找到[文件处理]-[加载],把[重新载入先前在启动时打开的文件]勾去掉,并确定就可以了. 附 ...