热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Oracle多表连接顺序与性能关系测试

一.创建表并insert数据createtableta(idnumber,namevarchar2(10));createtabletb(idnumber,jobvarchar

 

. 创建表并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;

 

 

--tb 在前

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

 

总结:    

       两条SQL 执行计划是一样的, tatb 的顺序没有影响。

       因为tatb 的记录相差较大,ta100万,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;

 

 

--ta 在前

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

 

总结:

       执行计划还是一样,不同的是表之间的关联模式发生的改变,从Hash Join 变成了Nested Loops

       Nested loop一般用在连接的表中有索引,并且索引选择性较好的时候. 在我们这个示例中,CBO 选择把返回结果集较小的表tb 作为outer tableCBO 下,默认把outer table 作为驱动表,然后用outer table 的每一行与inner table(我们这里是ta)进行Join,去匹配结果集。 由此可见,在tbinner table 有索引的情况,这种匹配就非常快。

 

       这种情况下整个SQLcost

       cost = outer access cost + (inner access cost * outer cardinality)

 

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

 

 

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;

 

 

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;

 

 

 

总结:

       2个就区别很明显。 因为Oraclesql的解析是从后向前的。 那么当先遇到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;

--tb 在前

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

 

总结:

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

      

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

 

 

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 来查看一下:

 

写全字段,执行时间是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表空间和数据文件的关系,否则拒绝申请


推荐阅读
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • Oracle Database 10g许可授予信息及高级功能详解
    本文介绍了Oracle Database 10g许可授予信息及其中的高级功能,包括数据库优化数据包、SQL访问指导、SQL优化指导、SQL优化集和重组对象。同时提供了详细说明,指导用户在Oracle Database 10g中如何使用这些功能。 ... [详细]
  • 本文详细介绍了在ASP.NET中获取插入记录的ID的几种方法,包括使用SCOPE_IDENTITY()和IDENT_CURRENT()函数,以及通过ExecuteReader方法执行SQL语句获取ID的步骤。同时,还提供了使用这些方法的示例代码和注意事项。对于需要获取表中最后一个插入操作所产生的ID或马上使用刚插入的新记录ID的开发者来说,本文提供了一些有用的技巧和建议。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • Oracle seg,V$TEMPSEG_USAGE与Oracle排序的关系及使用方法
    本文介绍了Oracle seg,V$TEMPSEG_USAGE与Oracle排序之间的关系,V$TEMPSEG_USAGE是V_$SORT_USAGE的同义词,通过查询dba_objects和dba_synonyms视图可以了解到它们的详细信息。同时,还探讨了V$TEMPSEG_USAGE的使用方法。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • Python SQLAlchemy库的使用方法详解
    本文详细介绍了Python中使用SQLAlchemy库的方法。首先对SQLAlchemy进行了简介,包括其定义、适用的数据库类型等。然后讨论了SQLAlchemy提供的两种主要使用模式,即SQL表达式语言和ORM。针对不同的需求,给出了选择哪种模式的建议。最后,介绍了连接数据库的方法,包括创建SQLAlchemy引擎和执行SQL语句的接口。 ... [详细]
  • 本文总结了Java中日期格式化的常用方法,并给出了示例代码。通过使用SimpleDateFormat类和jstl fmt标签库,可以实现日期的格式化和显示。在页面中添加相应的标签库引用后,可以使用不同的日期格式化样式来显示当前年份和月份。该文提供了详细的代码示例和说明。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • ALTERTABLE通过更改、添加、除去列和约束,或者通过启用或禁用约束和触发器来更改表的定义。语法ALTERTABLEtable{[ALTERCOLUMNcolu ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
author-avatar
情之秋梦痕
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有