关于oracle中的反连接(r3笔记第95天)
在之前的章节中见到讨论过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 |
-----------------------------------------------------------------------------
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Linux中怎么通过PID号找到对应的进程名及所在目录方法
- Apache环境下配置多个ssl证书搭建多个站点的方法
- 详解Linux手动释放缓存的方法
- 详解Centos7源码编译安装 php7.2之生产篇
- Centos7利用内存优化磁盘缓存读写速度的方法
- Redis过期策略和数据淘汰机制
- 图片由彩色渐变到黑白动画
- Kafka如何保证数据可靠性
- ubuntu中snap包的安装、更新删除与简单使用
- 学习在kernel态下使用NEON对算法进行加速的方法
- Centos7上Mesos和Marathon的安装和配置
- Redis高级数据类型-Bitmap和HyperLogLog
- Redis持久化策略
- 在linux下开启FTP服务方法介绍
- Linux中gpio接口的使用方法示例