一个普通的语句select * from t1, t2 where t1.id = t2.id and t1.name = 'a'; 这个语句在什么情况下最高效?

表连接分类:

1. 嵌套循环连接(Nested Loop Join)

2. 排序合并连接(Merge Sort Join):PGA 中的 SORT_AREA_SIZE 控制

3. 哈希连接(Hash Join):PGA中的HASH_AREA_SIZE控制

示例(基本均是在sql*plus环境下执行):

-- 准备脚本

prompt 准备实验环境.....

drop table t1 cascade constraints purge;

drop table t2 cascade constraints purge;

create table t1(

id       number not null,

n        number,

contents varchar2(4000)

);

create table t2(

id       number not null,

t1_id    number not null,

n        number,

contents varchar2(4000)

);

execute dbms_random.seed(0);

insert into t1

(

id,

n,

contents

)

select rownum,

rownum,

dbms_random.string('a', 50)

from dual

connect by level <= 100

order by dbms_random.random;

insert into t2

(

id,

t1_id,

n,

contents

)

select rownum,

rownum,

rownum,

dbms_random.string('b', 50)

from dual

connect by level <= 100000

order by dbms_random.random;

commit;

-- 修改统计参数为ALL

alter system set statistics_level=all;

-- alter session set statistics_level=all; 会话级修改,偶尔失效

show parameter statistics_level;

set linesize 1000

-- 实验开始

-- NL连接表

-- 1.1 HINT含义 leading(t1)表示强制先访问t1表,也就是t1表作为驱动表;use_nl表示强制用嵌套循环连接方式

select /*+leading(t1) use_nl(t2)*/ * from t1, t2 where t1.id = t2.t1_id;

select * from table(dbms_xplan.display_cursor(null, null, 'allstats last'));

starts列是表访问的次数,t1表访问了1次,t2表访问了100次

-- 1.2

select /*+leading(t1) use_nl(t2)*/ * from t1, t2 where t1.id = t2.t1_id and t1.n in (19, 20);

select * from table(dbms_xplan.display_cursor(null, null, 'allstats last'));

t1表访问了1次,t2表访问了2次

-- 1.3

select /*+leading(t1) use_nl(t2)*/ * from t1, t2 where t1.id = t2.t1_id and t1.n = 19;

select * from table(dbms_xplan.display_cursor(null, null, 'allstats last'));

t1表访问了1次,t2表访问了1次

-- 1.4

select /*+leading(t1) use_nl(t2)*/ * from t1, t2 where t1.id = t2.t1_id and t1.n = 9999999;

select * from table(dbms_xplan.display_cursor(null, null, 'allstats last'));

t1表访问了1次,t2表访问了0次

NL表连接访问次数最终结论:t1表的查询返回多少条记录,t2表就访问多少次。也就是,在嵌套循环连接中,驱动表返回多少条记录,被驱动表就访问多少次。

--2.  哈希连接表

-- 2.1

select /*+leading(t1) use_hash(t2)*/ * from t1, t2 where t1.id = t2.t1_id;

select * from table(dbms_xplan.display_cursor(null, null, 'allstats last'));

t1 1次, t2 1次

在HASH连接中,驱动表和被驱动表都只会访问1次或0次

-- 2.2

select /*+leading(t1) use_hash(t2)*/ * from t1, t2 where t1.id = t2.t1_id and t1.n = 9999999;

select * from table(dbms_xplan.display_cursor(null, null, 'allstats last'));

t1 1次,t2 0次

-- 2.3

select /*+leading(t1) use_hash(t2)*/ * from t1, t2 where t1.id = t2.t1_id and 1 = 2;

select * from table(dbms_xplan.display_cursor(null, null, 'allstats last'));

t1 0次, t2 0次

-- 3. 排序合并连接

select /*+ordered use_merge(t2)*/ * from t1, t2 where t1.id = t2.t1_id;

select * from table(dbms_xplan.display_cursor(null, null, 'allstats last'));

t1 1次, t2 1次

假设两张表,A和B,如果A作为驱动表,B作为被驱动表,那么,嵌套循环连接,就是先扫描A表,对A表的每一条记录,都扫描一遍B表;

而排序合并连接是 A表跟B表内的记录同时排序,然后连接; 哈希连接,类似排序合并,建立哈希表来连接。所以当返回少量记录的时候(OLTP系统常用,OLTP多用于电信、金融等系统),嵌套循环连接更高效,而返回大量记录的时候,排序合并连接跟哈希连接更高效。

各类连接驱动顺序区别

1.嵌套循环连接

select /*+leading(t1) use_nl(t2)*/ * from t1, t2 where t1.id = t2.t1_id and t1.n = 19;

select /*+leading(t2) use_nl(t1)*/ * from t1, t2 where t1.id = t2.t1_id and t1.n = 19;

select * from table(dbms_xplan.display_cursor(null, null, 'allstats last'));

t1表先访问:BUFFER 1014,t2只访问1次

t2表先访问:BUFFER 701K,t1被访问100000次

所以,嵌套循环连接要特别注意驱动表的顺序,小的结果集先访问,大的结果集后访问

2. 哈希连接

select /*+leading(t1) use_hash(t2)*/ * from t1, t2 where t1.id = t2.t1_id and t1.n = 19;

select /*+leading(t2) use_hash(t1)*/ * from t1, t2 where t1.id = t2.t1_id and t1.n = 19;

select * from table(dbms_xplan.display_cursor(null, null, 'allstats last'));

t1表先访问:BUFFER 1013,Used_Mem 286K,时间0.04秒

t2表先访问:BUFFER 1013,Used_Mem 11MB,时间0.01秒

在哈希连接中,驱动表顺序也很重要

3.排序合并连接

select /*+leading(t1) use_merge(t2)*/ * from t1, t2 where t1.id = t2.t1_id and t1.n = 19;

select /*+leading(t2) use_merge(t1)*/ * from t1, t2 where t1.id = t2.t1_id and t1.n = 19;

select * from table(dbms_xplan.display_cursor(null, null, 'allstats last'));

无论先访问t1还是先访问t2,效率都一样,执行时间,BUFFER,USED_MEM都一样,这表明,排序合并连接没有驱动表概念。

综上所述:嵌套循环连接和哈希连接有驱动顺序,驱动表的顺序不同将影响表连接的性能;而排序合并连接没有驱动的概念,无论哪张表在前都无妨。

嵌套循环连接不需要排序;哈希连接并不排序,消耗内存是用于建议HASH表;排序合并连接需要排序。关于哈希连接和排序合并连接,不要取多余的字段参与排序,因为选择的字段越少,消耗内存的尺寸就越小。

select /*+leading(t2) use_merge(t1)*/ * from t1, t2 where t1.id = t2.t1_id and t1.n = 19;

select /*+leading(t2) use_merge(t1)*/ t1.id from t1, t2 where t1.id = t2.t1_id and t1.n = 19;

select * from table(dbms_xplan.display_cursor(null, null, 'allstats last'));

可以看出Used_Mem代表的内存消耗差别很大

各类连接限制场景

explain plan for

select /*+leadind(t1) use_hash(t2)*/ *

from t1, t2

where t1.id <> t2.t1_id and t1.n = 19;

select * from table(dbms_xplan.display);

没有按照HINT的提示走HASH连接,而是走了NL连接

同理,可以试验> 、 < 、like,HASH都不支持。

explain plan for

select /*+leadind(t1) use_merge(t2)*/ *

from t1, t2

where t1.id <> t2.t1_id and t1.n = 19;

select * from table(dbms_xplan.display);

排序合并连接不支持<>和Like,但是支持>、<

嵌套循环无限制

表连接与索引

select /*+leading(t1) use_nl(t2)*/ * from t1, t2 where t1.id = t2.t1_id;

select * from table(dbms_xplan.display_cursor(null, null, 'allstats last'));

如果不使用HINT提示

select * from t1, t2 where t1.id = t2.t1_id;

select * from table(dbms_xplan.display_cursor(null, null, 'allstats last'));

会显示走的是HASH连接,因为ORACLE认为HASH连接更合理,但是此时逻辑读,COST值都差不多

create index t1_n on t1(n);

再执行上述语句,看看扫描t1表时BUFFER的差别

create index t2_t1_id on t2(t1_id);

再执行上述语句,看看扫描t2表时BUFFER的差别

最适合NL连接的场景:

1. 两表关联返回的记录不多,最佳情况是驱动结果集仅返回1条或少量几条记录,而被驱动表仅匹配到1条或少量几条记录,这种情况,即便t1和t2表的记录奇大无比,也是非常迅速的。

2. 遇到一些不等值查询导致哈希和排序合并连接被限制使用,不得不使用NL连接。

3.最佳的索引建立位置:驱动表的限制条件所在列有索引,被驱动表的连接条件所在列有索引。原因在于,驱动表的限制条件建立索引是为了缩小扫描驱动表的时 间,如果在驱动表的连接条件建立索引就没有任何意义了,所有列关联到另一个表的所有列,等同于每一条记录都要关联。而驱动表的限制条件建立了索引,只快速 返回1条或几条,然后再等传递给t2表的t1_id列,一般情况下,t2表对应t1表返回的记录并不多,所以t2表的t1_id列建索引是有意义的。

哈希连接与索引

连接条件的索引对哈希连接和排序合并连接起不到传递作用,对于哈希连接和排序合并连接来说,索引的连接条件建立索引起不到快速检索的作用,但是限制条件列如果有适合的索引可以快速检索到少量记录,还是可以提升性能的。

两表关联等值查询,ORACLE一般倾向于走HASH连接,因为HASH连接算法本身比较高效,也比较先进。优化HASH连接可以通过调整PGA中的HASH_AREA_SIZE来优化,如果PGA是自动管理的,那么就增大PGA的大小。

排序合并连接与索引

排序合并连接上的连接条件虽然没有检索作用,但是有消除排序的作用,因此可以提高效率。

SQL>select /*+ordered use_merge(t2)*/

*

from t1, t2

where t1.id = t2.t1_id;

SQL>select * from table(dbms_xplan.display_cursor(null, null, 'allstats last'));

create index idx_t1 on t1(id);

create index idx_t1_id on t2(t1_id); -- 上面建立过

SQL>select /*+ordered use_merge(t2)*/

*

from t1, t2

where t1.id = t2.t1_id;

SQL>select * from table(dbms_xplan.display_cursor(null, null, 'allstats last'));

建了两个索引,最终只用到了一个,这是ORACLE排序合并连接的BUG,在11G官方文档里有承认。

也可以通过增大PGA,优化排序合并连接

Oracle表连接的更多相关文章

  1. oracle表连接------&gt;排序合并连接(Merge Sort Join)

    排序合并连接 (Sort Merge Join)是一种两个表在做连接时用排序操作(Sort)和合并操作(Merge)来得到连接结果集的连接方法. 对于排序合并连接的优缺点及适用场景例如以下: a,通常 ...

  2. oracle表连接的优化

    多表连接的三种方式: HASH JOIN,MERGE JOIN,NESTED LOOP NESTED LOOP: 嵌套循环连接,适用于内表数据量较小时.外表返回的每一行都要在内表中检索找到与它匹配的行 ...

  3. Oracle 表连接

    Oracle 表之间的连接分为三种: 1. 内连接(自然连接) 2. 外连接 (1)左外连接 (左边的表不加限制)      (2)右外连接(右边的表不加限制)      (3)全外连接(左右两表都不 ...

  4. Oracle 表连接方式分析 .

    一 引言 数据仓库技术是目前已知的比较成熟和被广泛采用的解决方案,用于整和电信运营企业内部所有分散的原始业务数据,并通过便捷有效的数据访问手段,可以支持企业内部不同部门,不同需求,不同层次的用户随时获 ...

  5. ORACLE 表连接详解

    在ORACLE中,表连接方式主要有:内连接,外连接,自连接: 内连接: 这是最常用的连接查询 SELECT * FROM A INNER JOIN B ON A.ID=B.ID SELECT * FR ...

  6. Oracle表连接总结

    1 简述 1) 两个表的连接,是通过将一个表中的一列或者多列同另一个表中的列链接而建立起来的.用来连接两张表的表达式组成了连接条件.当连接成功后,第二张表中的数据就同第一张表连接起来了,并形成了复合结 ...

  7. oracle 表连接 - hash join 哈希连接

    一. hash 连接(哈希连接)原理 指的是两个表连接时, 先利用两表中记录较少的表在内存中建立 hash 表, 然后扫描记录较多的表并探測 hash 表, 找出与 hash 表相匹配的行来得到结果集 ...

  8. 知识点:Oracle+表连接方式(内连接-外连接-自连接)+详解 来自百度文库

    Oracle 表之间的连接分为三种: 1. 内连接(自然连接) 2. 外连接 (1)左外连接 (左边的表不加限制)        (2)右外连接(右边的表不加限制)        (3)全外连接(左右 ...

  9. Oracle表连接(转)

    表之间的连接 Join是一种试图将两个表结合在一起的谓词,一次只能连接2个表,表连接也可以被称为表关联.在后面的叙述中,我们将会使用”row source”来代替”表”,因为使用row source更 ...

随机推荐

  1. sql 自定义函数--固定格式字符转时间类型

    遇到一个德国的客户,他们的时间格式是JJJJ-TT-DD HH:MM:SS,程序按照这个格式将时间插入数据库,但是在sql自带的转换函数convert.cast过程中报错,网上搜了下都说用conver ...

  2. hdu1565 网络流或状态压缩DP

    对于网络流有一个定理: 最小点权覆盖集=最大网络流: 最大点权独立集=总权值-最小点权覆盖集: 网络流解法代码如下: #include<cstdio> #include<iostre ...

  3. CF 19D - Points 线段树套平衡树

    题目在这: 给出三种操作: 1.增加点(x,y) 2.删除点(x,y) 3.询问在点(x,y)右上方的点,如果有相同,输出最左边的,如果还有相同,输出最低的那个点 分析: 线段树套平衡树. 我们先离散 ...

  4. extjs的调试方法

    1.使用extjs自带的测试工具 第一步:在ExtJS下载的资源包中,找到debug.js,将JS文件导入实际要运行的HTML或者JSP页面上 第二步:在有关JS文件代码中嵌入Ext.log('自定义 ...

  5. Android之Android studio安装

    首先下载jdk:http://www.oracle.com/technetwork/java/javase/downloads/index.html 然后下载Android SDK:http://ww ...

  6. 使用C#动态生成Word文档/Excel文档的程序测试通过后,部署到IIS服务器上,不能正常使用的问题解决方案

    使用C#动态生成Word文档/Excel文档的程序功能调试.测试通过后,部署到服务器上,不能正常使用的问题解决方案: 原因: 可能asp.net程序或iis访问excel组件时权限不够(Ps:Syst ...

  7. php对UTF8字体串进行单字分割返回数组

    在网上查了很多字符串分割方法,都无法正确对UTF8字符串进行正确分割返回单个字符的数组.经过对FTU8编码的分析写出了下面的方法对UTF8进行分割.本人测试可用.本方法只支持UTF8编码的,其它编码转 ...

  8. 打造属于自己的安卓Metro界面

    前言: 各位小伙伴,又到了每周更新文章了时候了,本来是周日能发出来呢,这不是赶上清明节吗,女王大人发话了,清明节前两天半陪她玩,只留给我周一下午半天时间写博客,哪里有女王哪里就有压迫呀有木有!好了闲话 ...

  9. 第四十一篇、Masonry利用Block实现链式编程

    一直都觉得使用Masonry的时候语法特别优雅,很早的时候就想尝试下怎么实现, 一直都没弄明白,直到最近看见一篇叫block实现链式编程的 1.方法的返回类型是代码块 >代码块的返回类型是该类的 ...

  10. HTML+CSS学习笔记(5)- 与浏览者交互,表单标签

    HTML+CSS学习笔记(5)- 与浏览者交互,表单标签 1.使用表单标签,与用户交互 网站怎样与用户进行交互?答案是使用HTML表单(form).表单是可以把浏览者输入的数据传送到服务器端,这样服务 ...