设为首页 收藏本站
查看: 765|回复: 0

[经验分享] Oracle 多表 连接 顺序 与 性能关系 测试

[复制链接]
YunVN网友  发表于 2016-8-17 06:23:38 |阅读模式
. 创建表并insert 数据
create table ta (id number,name varchar2(10));

create table tb(id number,job varchar2(10));

begin

for i in 1..1000000 loop

begin

insert into ta values(i,'dave');

commit;

end;

end loop;

end;

begin

for i in 1..1000000 loop

begin

if i<10 then

insert into tb values(i,'boy');

elsif i<20 and i>10 then

insert into tb values(i,'girl');

commit;

end if;

end;

end loop;

end;

.在没有索引的情况关联ta tb 查询

相关链接:
Oracle Optimizer CBO RBO
http://blog.csdn.net/tianlesoftware/archive/2010/08/19/5824886.aspx

多表连接的三种方式详解 HASH JOIN MERGE JOIN NESTED LOOP
http://blog.csdn.net/tianlesoftware/archive/2010/08/21/5826546.aspx

Oracle Hint
http://blog.csdn.net/tianlesoftware/archive/2010/08/23/5833020.aspx

2.1 optimizer选择 CBO10g 默认)
--ta 在前
select ta.id, ta.name,tb.job from ta,tb where ta.id=tb.id;


  
DSC0000.gif



--tb 在前
select ta.id, ta.name,tb.job from tb,ta where ta.id=tb.id;
  


DSC0001.gif

总结:
两条SQL 执行计划是一样的, ta和tb 的顺序没有影响。
因为ta和tb 的记录相差较大,ta是100万,tb 只有20条。 所以这里CBO 选择使用Hash Join。
CBO 选择2个表中记录较小的表tb,将其数据放入内存,对Join key构造hash 表,然后去扫描大表ta。 找出与散列表匹配的行。

2.2 tatb ID b-tree 索引后在查看

--建索引
create index idx_ta_id on ta(id);

create index idx_tb_id on tb(id);

--tb 在前
select ta.id, ta.name,tb.job from tb,ta where ta.id=tb.id;


DSC0002.gif

--ta 在前
select ta.id, ta.name,tb.job from ta,tb where ta.id=tb.id;


DSC0003.gif

总结:
执行计划还是一样,不同的是表之间的关联模式发生的改变,从Hash Join 变成了Nested Loops。
Nested loop一般用在连接的表中有索引,并且索引选择性较好的时候. 在我们这个示例中,CBO 选择把返回结果集较小的表tb 作为outer table,CBO 下,默认把outer table 作为驱动表,然后用outer table 的每一行与inner table(我们这里是ta)进行Join,去匹配结果集。 由此可见,在tb(inner table) 有索引的情况,这种匹配就非常快。

这种情况下整个SQLcost
cost = outer access cost + (inner access cost * outer cardinality)

从某种角度上看,可以把Nested loop 看成2层for 循环。


2.3 使用RBO 查看
在10g里,optimizer 默认已经使用CBO了,如果我们想使用RBO, 只能通过Hint 来实现。

-- ta 在前
select /*+rule*/ta.id, ta.name,tb.job from ta,tb where ta.id=tb.id;


DSC0004.gif


SYS@anqing2(rac2)> select /*+rule*/ta.id, ta.name,tb.job from ta,tb where ta.id<100 and ta.id=tb.id;
Elapsed: 00:00:00.00
-- 注意这个SQL里,我们加了ta.id<100 的条件
Execution Plan
----------------------------------------------------------
Plan hash value: 3943212106

---------------------------------------------------
| Id | Operation | Name |
---------------------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | TABLE ACCESS BY INDEX ROWID | TB |
| 2 | NESTED LOOPS | |
| 3 | TABLE ACCESS BY INDEX ROWID| TA |
|* 4 | INDEX RANGE SCAN | IDX_TA_ID |
|* 5 | INDEX RANGE SCAN | IDX_TB_ID |
---------------------------------------------------
-- 当我们加上条件之后,就先走ta了,而不是tb 因为先走ta,用ta的限制条件过滤掉一部分结果,这样剩下的匹配工作就会减少。
Predicate Information (identified by operation id):
---------------------------------------------------

4 - access("TA"."ID"<100)
5 - access("TA"."ID"="TB"."ID")

Note
-----
- rule based optimizer used (consider using cbo)

--tb 在前
select /*+rule*/ta.id, ta.name,tb.job from tb,ta where ta.id=tb.id;


DSC0005.gif


总结:
这2个就区别很明显。 因为Oracle对sql的解析是从后向前的。 那么当先遇到tb时,那么会对tb进行全表扫描,然后用这个结果匹配ta。因为ta有索引,所以通过索引去匹配。
如果先遇到ta,那么就会对ta进行全表扫描。 因为2个表的差距很大,所以全表扫描的成本也就很大。
所以在RBO 下,大表在前,小表在后。这样就会先遇到小表,后遇到大表。 如果有指定限定的where 条件,会先走限定条件的表。


2.4 drop 索引之后,在走RBO

drop index idx_ta_id;

drop index idx_tb_id;


--ta 在前
select /*+rule*/ta.id, ta.name,tb.job from ta,tb where ta.id=tb.id;

DSC0006.gif


--tb 在前
select /*+rule*/ta.id, ta.name,tb.job from tb,ta where ta.id=tb.id;


DSC0007.gif

总结:
这里选择了Sort Merge Join 来连接2张表。Sort Merge join 用在没有索引,并且数据已经排序的情况.

我们表中的记录是按照顺序插叙的,所以符合这个条件。 SQL 的解析还是按照从后往前,所以这里ta和tb 在前先扫描的顺序不一样,不过都是全表扫描。 效率都不高。


2.5 引深一个问题:使用 字段名 代替 *
* 能方便很多,但在ORACLE解析的过程中, 会通过查询数据字典,会将’*’ 依次转换成所有的列名,这就需要耗费更多的时间. 从而降低了效率。

SYS@anqing2(rac2)> set timing on
SYS@anqing2(rac2)> select * from ta where rownum=1;
ID NAME
---------- ----------
1 dave

Elapsed: 00:00:00.03
SYS@anqing2(rac2)> desc ta
Name Null? Type
----------------------------------------- -------- ----------------------------
ID NUMBER
NAME VARCHAR2(10)

SYS@anqing2(rac2)> select id,name from ta where rownum=1;

ID NAME
---------- ----------
1 dave

Elapsed: 00:00:00.02

时间已经缩短。 但不明显,用Toad 来查看一下:

DSC0008.gif


写全字段,执行时间是161 毫秒,用* 561毫秒。 差距很明显。

查看一下他们的执行计划:
SYS@anqing2(rac2)> select * from ta where rownum=1;
Elapsed: 00:00:00.00

Execution Plan
----------------------------------------------------------
Plan hash value: 761731071

---------------------------------------------------------------------------
| id | operation | name | rows | bytes | cost (%cpu)| time |
---------------------------------------------------------------------------
| 0 | select statement | | 1 | 20 | 7 (72)| 00:00:01 |
|* 1 | count stopkey | | | | | |
| 2 | table access full| ta | 890k| 16m| 7 (72)| 00:00:01 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter(ROWNUM=1)

Note
-----
- dynamic sampling used for this statement

SYS@anqing2(rac2)> select id,name from ta where rownum=1;
Elapsed: 00:00:00.00
Execution Plan
----------------------------------------------------------
Plan hash value: 761731071

---------------------------------------------------------------------------
| id | operation | name | rows | bytes | cost (%cpu)| time |
---------------------------------------------------------------------------
| 0 | select statement | | 1 | 20 | 7 (72)| 00:00:01 |
|* 1 | count stopkey | | | | | |
| 2 | table access full| ta | 890k| 16m| 7 (72)| 00:00:01 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter(ROWNUM=1)

Note
-----
- dynamic sampling used for this statement


注意:
使用 * 和 写全字段名,他们的执行计划是一样的,但是执行时间不一样。







-------------------------------------------------------------------------------------------------------
Blog: http://blog.csdn.net/tianlesoftware
Email: dvd.dba@gmail.com
DBA1 群:62697716(满); DBA2 群:62697977(满) DBA3 群:62697850(满)
DBA 超级群:63306533(满); DBA4 群: 83829929 DBA5群: 142216823
DBA6 群:158654907 聊天 群:40132017 聊天2群:69087192
--加群需要在备注说明Oracle表空间和数据文件的关系,否则拒绝申请

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-258703-1-1.html 上篇帖子: 《Oracle编程艺术》学习笔记(1)-配置环境 下篇帖子: 原创 oracle 数据完整性总结<十>
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表