[转帖]关于semi-join/anti-join的一点探讨_MySQL, Oracle及数据库讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  MySQL, Oracle及数据库讨论区 »
总帖数
1
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 4029 | 回复: 0   主题: [转帖]关于semi-join/anti-join的一点探讨        下一篇 
derek
注册用户
等级:中校
经验:1550
发帖:209
精华:0
注册:2011-7-21
状态:离线
发送短消息息给derek 加好友    发送短消息息给derek 发消息
发表于: IP:您无权察看 2011-8-1 21:34:25 | [全部帖] [楼主帖] 楼主

Semi-join通常出现在使用了exists或in的 sql中,所谓semi-join即在两表关联时,当第二个表中存在一个或多个匹配记录时,返回第一个表的记录;
与普通join的区别在于semi-join时,第一个表里的记录最多只返回一次;

create table dept (deptno number(5), dname varchar2(20));
create table emp (deptno number(5), name varchar2(20));
insert into dept values(1,'IT');
insert into dept values(2,'HR');
insert into dept values(3,'Marketplace');
insert into emp values(1,'justin1');
insert into emp values(1,'justin2');
insert into emp values(2,'justin3');
commit;


例1

现在要查询出职员不为空的部门
普通的表连接会返回重复值

SQL> select d.deptno,d.dname from dept d, emp e where e.deptno = d.deptno;
DEPTNO DNAME
------ --------------------
1 IT
1 IT
2 HR
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     3 |   114 |     7  (15)| 00:00:01 |
|*  1 |  HASH JOIN         |      |     3 |   114 |     7  (15)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| DEPT |     3 |    75 |     3   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| EMP  |     3 |    39 |     3   (0)| 00:00:01 |
---------------------------------------------------------------------------


而使用semi-join时候,不会返回重复记录

SQL> select d.deptno,d.dname from dept d where d.deptno in (select e.deptno from emp e);
DEPTNO DNAME
------ --------------------
1 IT
2 HR
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     2 |    76 |     7  (15)| 00:00:01 |
|*  1 |  HASH JOIN SEMI    |      |     2 |    76 |     7  (15)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| DEPT |     3 |    75 |     3   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| EMP  |     3 |    39 |     3   (0)| 00:00:01 |
---------------------------------------------------------------------------


而anti-join则与semi-join相反,即当在第二张表没有发现匹配记录时,才会返回第一张表里的记录;
当使用not exists/not in的时候会用到,两者在处理null值的时候会有所区别

例2
查询职员为空的部门
普通sql

SQL> select d.deptno,d.dname from dept d
2  minus
3  select d.deptno,d.dname from dept d where d.deptno in (select e.deptno from emp e);
DEPTNO DNAME
------ --------------------
3 Marketplace
-----------------------------------------------------------------------------
| Id  | Operation            | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |      |     3 |   189 |    12  (75)| 00:00:01 |
|   1 |  MINUS               |      |       |       |            |          |
|   2 |   SORT UNIQUE        |      |     3 |    75 |     4  (25)| 00:00:01 |
|   3 |    TABLE ACCESS FULL | DEPT |     3 |    75 |     3   (0)| 00:00:01 |
|   4 |   SORT UNIQUE        |      |     3 |   114 |     8  (25)| 00:00:01 |
|*  5 |    HASH JOIN         |      |     3 |   114 |     7  (15)| 00:00:01 |
|   6 |     TABLE ACCESS FULL| EMP  |     3 |    39 |     3   (0)| 00:00:01 |
|   7 |     TABLE ACCESS FULL| DEPT |     3 |    75 |     3   (0)| 00:00:01 |
-----------------------------------------------------------------------------


使用anti join

SQL> select d.deptno,d.dname from dept d
2  where d.deptno not in (select deptno from emp);
DEPTNO DNAME
------ --------------------
3 Marketplace
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     3 |   114 |     7  (15)| 00:00:01 |
|*  1 |  HASH JOIN ANTI NA |      |     3 |   114 |     7  (15)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| DEPT |     3 |    75 |     3   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| EMP  |     3 |    39 |     3   (0)| 00:00:01 |
---------------------------------------------------------------------------


而换成not exists时候,hash join anti na变成了hash join anti

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     3 |   114 |     7  (15)| 00:00:01 |
|*  1 |  HASH JOIN ANTI    |      |     3 |   114 |     7  (15)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| DEPT |     3 |    75 |     3   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| EMP  |     3 |    39 |     3   (0)| 00:00:01 |
---------------------------------------------------------------------------


Not exists与not in的区别在于not exists不受null值影响

Oracle在解析sql时候会尽可能的把子查询转换为表连接
Oracle在以下情况不会使用semi-join
1、    使用distinct或者union
2、    Exists/in子查询中使用了or

Hash_sj/merge_sj/nl_sj都是关于semi-join的一些hint




赞(0)    操作        顶端 
总帖数
1
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论