关于oracle中的反连接

简介: 在之前的章节中见到讨论过oracle中的半连接 http://blog.itpub.net/23718752/viewspace-1334483/ 与半连接相对应的是反连接,简而言之半连接就是查询条件中的in,exists,反连接就是not in, not exists这种类型的连接。

在之前的章节中见到讨论过oracle中的半连接 http://blog.itpub.net/23718752/viewspace-1334483/
与半连接相对应的是反连接,简而言之半连接就是查询条件中的in,exists,反连接就是not in, not exists这种类型的连接。
在asktom中,tom也对大家关心的in,exists,not in, not exists的问题进行了大量的佐证和解释。因为问题是在2001年左右提出来的,当时还是oracle 8的时代,帖子也沉里许久,在2013年的时候,tom在自己的博客中做了全新的解释,说大家都在讨论十几年前的东西了。传统RBO中的in,exists鲜明的对比在CBO中也都做了统一的优化处理,使得我们能够更加专注于程序的逻辑实现。

Followup   May 6, 2013 - 7pm UTC:

you are correct that things change and we actually discussed this in the body of this very very very long posting (13 years of posting ;) ) 

it is the difference between the CBO and RBO. 

here is a more formal write up of it: 

http://www.oracle.com/technetwork/issue-archive/2006/06-may/o36asktom-099107.html
对于反连接,我准备用下面的实例来进行简单的演示。
首先创建两个测试表,我们使用最熟悉的emp,dept表。为了保留原有的数据,我重新创建了两个新的表,因为dept表中的数据太少,我就特意添加了一列数据使得效果更加明显。
create table emp as select *from scott.emp;
create table dept as select *from scott.dept;
insert into dept values(50,'IT','BEIJING');
commit;
emp表中的数据情况如下,deptno目前只分布在3个部门。
SQL> select deptno from emp group by deptno;

    DEPTNO
----------
        30
        20
        10
dept表中有5个部们,那么部门40,50就是emp中不存在的。也就是目前还没有员工在deptno 40,50两个部门。
SQL> select deptno,dname from dept;

    DEPTNO DNAME
---------- --------------
        10 ACCOUNTING
        20 RESEARCH
        30 SALES
        40 OPERATIONS
        50 IT

我们来使用常用的反连接形式。查得deptno 为40,50的记录。这也是我们预期的。
select *from dept where not exists(select null from emp where emp.deptno=dept.deptno);

    DEPTNO DNAME          LOC
---------- -------------- -------------
        50 IT             BEIJING
        40 OPERATIONS     BOSTON
查看执行计划,能够清晰的看到对应的反连接表示anti,当然我们也可以通过hint /*+hash_aj*/来指定为hash 反连接。

Execution Plan
----------------------------------------------------------
Plan hash value: 474461924

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     5 |   215 |     8  (13)| 00:00:01 |
|*  1 |  HASH JOIN ANTI    |      |     5 |   215 |     8  (13)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| DEPT |     5 |   150 |     4   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| EMP  |    14 |   182 |     3   (0)| 00:00:01 |
---------------------------------------------------------------------------

我们来看看和not exists等价的not in 形式。注意我在子查询中添加了deptno is not null,如果没有这个条件,not in和not exists是不等价的。如果emp中存在deptno为空的记录,那么整个查询就会返回0行。

SQL> select *from dept where deptno not in (select deptno from emp where deptno is not null);

    DEPTNO DNAME          LOC
---------- -------------- -------------
        50 IT             BEIJING
        40 OPERATIONS     BOSTON
执行计划如下。
Execution Plan
----------------------------------------------------------
Plan hash value: 810774822

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     5 |   215 |     8  (13)| 00:00:01 |
|*  1 |  HASH JOIN ANTI SNA|      |     5 |   215 |     8  (13)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| DEPT |     5 |   150 |     4   (0)| 00:00:01 |
|*  3 |   TABLE ACCESS FULL| EMP  |    14 |   182 |     3   (0)| 00:00:01 |
---------------------------------------------------------------------------
下面的这种形式可能也是大家常犯的一个错误,就是会把原本的not in,not exists查询改写为下面的形式。结果就出乎意料了

select dept.* from dept,emp where dept.deptno!=emp.deptno;
。。。。

    DEPTNO DNAME          LOC
---------- -------------- -------------
        50 IT             BEIJING
        50 IT             BEIJING
        50 IT             BEIJING
        50 IT             BEIJING
        50 IT             BEIJING
        50 IT             BEIJING
        50 IT             BEIJING
        50 IT             BEIJING
        50 IT             BEIJING
        50 IT             BEIJING
        50 IT             BEIJING

    DEPTNO DNAME          LOC
---------- -------------- -------------
        50 IT             BEIJING

56 rows selected.
可以从执行计划中看到,直接是对emp,dept做了nested loop join,这种错误需要避免。
Execution Plan
----------------------------------------------------------
Plan hash value: 4192419542

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |    56 |  2408 |    11   (0)| 00:00:01 |
|   1 |  NESTED LOOPS      |      |    56 |  2408 |    11   (0)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| DEPT |     5 |   150 |     4   (0)| 00:00:01 |
|*  3 |   TABLE ACCESS FULL| EMP  |    11 |   143 |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------


-->not in ,not exists的等价实现
在实际的工作中,可能根据需要还会对not in ,not exists改写为其他的等价形式。比如使用下面的形式。
SQL> select dept.* from dept ,emp where dept.deptno=emp.deptno(+) and emp.deptno is null;

    DEPTNO DNAME          LOC
---------- -------------- -------------
        50 IT             BEIJING
        40 OPERATIONS     BOSTON
执行计划如下:
Execution Plan
----------------------------------------------------------
Plan hash value: 474461924

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     5 |   215 |     8  (13)| 00:00:01 |
|*  1 |  HASH JOIN ANTI    |      |     5 |   215 |     8  (13)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| DEPT |     5 |   150 |     4   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| EMP  |    14 |   182 |     3   (0)| 00:00:01 |
---------------------------------------------------------------------------

下面的这种形式化就对结果集进行了筛查。如果emp中的deptno为空,就设定一个不存在的deptno值。
select dept.* from dept where deptno not in (select nvl(deptno,'-1') from emp)
SQL> /

    DEPTNO DNAME          LOC
---------- -------------- -------------
        50 IT             BEIJING
        40 OPERATIONS     BOSTON
执行计划如下:
Execution Plan
----------------------------------------------------------
Plan hash value: 810774822

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     5 |   215 |     8  (13)| 00:00:01 |
|*  1 |  HASH JOIN ANTI SNA|      |     5 |   215 |     8  (13)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| DEPT |     5 |   150 |     4   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| EMP  |    14 |   182 |     3   (0)| 00:00:01 |
---------------------------------------------------------------------------
还可以使用集合来实现。不过这种方式使用的场景要少一些。这种方式直接把not in改换成了in的格式。
SQL> select *from dept where deptno in (select deptno from dept minus select deptno from emp);

    DEPTNO DNAME          LOC
---------- -------------- -------------
        40 OPERATIONS     BOSTON
        50 IT             BEIJING
执行计划如下:
Execution Plan
----------------------------------------------------------
Plan hash value: 3106111345

-----------------------------------------------------------------------------
| Id  | Operation            | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |      |     5 |   150 |    13  (16)| 00:00:01 |
|*  1 |  FILTER              |      |       |       |            |          |
|   2 |   TABLE ACCESS FULL  | DEPT |     5 |   150 |     4   (0)| 00:00:01 |
|   3 |   MINUS              |      |       |       |            |          |
|   4 |    SORT UNIQUE NOSORT|      |     1 |    13 |     5  (20)| 00:00:01 |
|*  5 |     TABLE ACCESS FULL| DEPT |     1 |    13 |     4   (0)| 00:00:01 |
|   6 |    SORT UNIQUE NOSORT|      |     1 |    13 |     4  (25)| 00:00:01 |
|*  7 |     TABLE ACCESS FULL| EMP  |     1 |    13 |     3   (0)| 00:00:01 |
-----------------------------------------------------------------------------


目录
相关文章
|
Oracle 关系型数据库 流计算
Flink CDC不支持直接连接到Oracle ADG备库进行数据同步
Flink CDC不支持直接连接到Oracle ADG备库进行数据同步
903 1
|
SQL Oracle 关系型数据库
本机不安装Oracle客户端,使用PL/SQL Developer连接远程数据库
本机不安装Oracle客户端,使用PL/SQL Developer连接远程数据库
1446 0
|
Oracle 关系型数据库 数据库
已解决:idea 连接 oracle 数据库 避雷
已解决:idea 连接 oracle 数据库 避雷
401 2
|
Oracle 安全 关系型数据库
【Oracle】使用Navicat Premium连接Oracle数据库两种方法
以上就是两种使用Navicat Premium连接Oracle数据库的方法介绍,希望对你有所帮助!
2844 28
|
SQL Oracle 关系型数据库
Python连接Oracle
Python连接Oracle
431 0
|
Oracle Java 关系型数据库
SpringBoot整合Mybatis连接Oracle数据库
SpringBoot整合Mybatis连接Oracle数据库
SpringBoot整合Mybatis连接Oracle数据库
|
Oracle 数据可视化 关系型数据库
Oracle数据库安装及使用Navicat连接oracle2
Oracle数据库安装及使用Navicat连接oracle
323 1
|
Oracle 关系型数据库 数据库
实时计算 Flink版产品使用问题之连接到Oracle数据库但无法读取到数据,是什么导致的
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
Oracle Java 关系型数据库
【服务器】python通过JDBC连接到位于Linux远程服务器上的Oracle数据库
【服务器】python通过JDBC连接到位于Linux远程服务器上的Oracle数据库
709 6
|
Oracle 关系型数据库
Navicat 连接Oracle ORA-28547: connection to server failed, probable Oracle Net admin error
Navicat 连接Oracle ORA-28547: connection to server failed, probable Oracle Net admin error
2074 0

推荐镜像

更多