https://blog.csdn.net/dataminer_2007/article/details/41907581
一. sort merge joins连接(排序合并连接) 原理

指的是两个表连接时, 通过连接列先分别排序后, 再通过合并操作来得到最后返回的结果集的方法.

假如表 T1 和 T2 的连接方式是排序合并连接, oracle 执行步骤如下:
(1) 根据 sql 语句中的谓词条件(如果有) 访问 T1 表, 得到一个过滤的结果集, 然后按照 T1 中的连接列对结果集进行排序
(2) 根据 sql 语句中的谓词条件(如果有) 访问 T2 表, 得到一个过滤的结果集, 然后按照 T2 中的连接列对结果集进行排序
(3) 将 1 和 2 的结果集合并起来, 对记录进行匹配得到最后的结果集.

通常来说, sort merge joins连接(排序合并连接) 使用并不广泛, 因为在大部分情况下使用 nested loops 或者 hash joins 都能获得比它更好的执行效率,
但是由于 hash joins 只能用于等值连接条件, 所以在非等值条件连接以及非 like 非 "<>" 情况下, 如果连接列上已经有排序, 使用 sort merge joins连接方式能获得比较好的执行效率

二. sort merge joins连接(排序合并连接) 特性

(1) 驱动表最多访问一次, 如果独立的谓词条件(不涉及驱动表字段的函数或者表达式等)不成立, 则不用再去访问驱动表
(2) 被驱动表最多访问一次. 如果驱动表没有记录, 被驱动表不用访问
(3) 驱动表的选择对于执行成本以及性能没有太大的影响
(4) 支持大部分的连接条件, 比如 ">" "<" ">=" "<=", 不支持 like, "<>"

构造试验数据
SQL> CREATE TABLE t1 (
id NUMBER NOT NULL,
n NUMBER,
pad VARCHAR2(4000),
CONSTRAINT t1_pk PRIMARY KEY(id)
);

Table created.

SQL> CREATE TABLE t2 (
id NUMBER NOT NULL,
t1_id NUMBER NOT NULL,
n NUMBER,
pad VARCHAR2(4000),
CONSTRAINT t2_pk PRIMARY KEY(id),
CONSTRAINT t2_t1_fk FOREIGN KEY (t1_id) REFERENCES t1
);

Table created.

SQL> CREATE TABLE t3 (
id NUMBER NOT NULL,
t2_id NUMBER NOT NULL,
n NUMBER,
pad VARCHAR2(4000),
CONSTRAINT t3_pk PRIMARY KEY(id),
CONSTRAINT t3_t2_fk FOREIGN KEY (t2_id) REFERENCES t2
);

Table created.
SQL> CREATE TABLE t4 (
id NUMBER NOT NULL,
t3_id NUMBER NOT NULL,
n NUMBER,
pad VARCHAR2(4000),
CONSTRAINT t4_pk PRIMARY KEY(id),
CONSTRAINT t4_t3_fk FOREIGN KEY (t3_id) REFERENCES t3
);

Table created.

SQL> execute dbms_random.seed(0)

PL/SQL procedure successfully completed.

SQL> INSERT INTO t1 SELECT rownum, rownum, dbms_random.string('a',50) FROM dual CONNECT BY level <= 10 ORDER BY dbms_random.random;

10 rows created.

SQL> INSERT INTO t2 SELECT 100+rownum, t1.id, 100+rownum, t1.pad FROM t1, t1 dummy ORDER BY dbms_random.random;

100 rows created.

SQL> INSERT INTO t3 SELECT 1000+rownum, t2.id, 1000+rownum, t2.pad FROM t2, t1 dummy ORDER BY dbms_random.random;

1000 rows created.

SQL> INSERT INTO t4 SELECT 10000+rownum, t3.id, 10000+rownum, t3.pad FROM t3, t1 dummy ORDER BY dbms_random.random;

10000 rows created.

SQL> COMMIT;

Commit complete.
使用 hint 让执行计划以 T3 作为驱动表
SQL> select /*+ leading(t3) use_merge(t4) */ * from t3, t4 where t3.id = t4.t3_id and t3.n = 1100;
10 rows selected.

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

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------
SQL_ID g0rdyg9hdh9m0, child number 0
-------------------------------------
select /*+ leading(t3) use_merge(t4) */ * from t3, t4 where t3.id = t4.t3_id and t3.n = 1100

Plan hash value: 3831111046
-----------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
-----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 10 |00:00:00.02 | 119 | | | |
| 1 | MERGE JOIN | | 1 | 10 | 10 |00:00:00.02 | 119 | | | |
| 2 | SORT JOIN | | 1 | 1 | 1 |00:00:00.01 | 15 | 2048 | 2048 | 2048 (0)|
|* 3 | TABLE ACCESS FULL| T3 | 1 | 1 | 1 |00:00:00.01 | 15 | | | |
|* 4 | SORT JOIN | | 1 | 10000 | 10 |00:00:00.02 | 104 | 974K| 535K| 865K (0)|
| 5 | TABLE ACCESS FULL| T4 | 1 | 10000 | 10000 |00:00:00.01 | 104 | | | |
-----------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("T3"."N"=1100)
4 - access("T3"."ID"="T4"."T3_ID")
filter("T3"."ID"="T4"."T3_ID")
使用 hint 让执行计划以 T4 作为驱动表
SQL> select /*+ leading(t4) use_merge(t3) */ * from t3, t4 where t3.id = t4.t3_id and t3.n = 1100;
10 rows selected.

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

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------
SQL_ID gxuwn06y1c1az, child number 0
-------------------------------------
select /*+ leading(t4) use_merge(t3) */ * from t3, t4 where t3.id = t4.t3_id and t3.n = 1100
Plan hash value: 875334572
-----------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
-----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 10 |00:00:00.04 | 119 | | | |
| 1 | MERGE JOIN | | 1 | 10 | 10 |00:00:00.04 | 119 | | | |
| 2 | SORT JOIN | | 1 | 10000 | 1001 |00:00:00.04 | 104 | 974K| 535K| 865K (0)|
| 3 | TABLE ACCESS FULL| T4 | 1 | 10000 | 10000 |00:00:00.01 | 104 | | | |
|* 4 | SORT JOIN | | 1001 | 1 | 10 |00:00:00.01 | 15 | 2048 | 2048 | 2048 (0)|
|* 5 | TABLE ACCESS FULL| T3 | 1 | 1 | 1 |00:00:00.01 | 15 | | | |
-----------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("T3"."ID"="T4"."T3_ID")
filter("T3"."ID"="T4"."T3_ID")
5 - filter("T3"."N"=1100)
从返回的执行计划结果中我们可以看到:
1. 以 T3 为驱动表和以 T4 为驱动表, 两者的 cost (A-Time) 和 buffers 都差不多
2. 以 T3 为驱动表时, T3 访问一次, T4 也是访问一次; 以 T4 为驱动表时, T4 访问一次, T3 也是访问一次
3. 需要排序, 如果 PGA 空间重足时在 PGA 中排序, 不如果不足则交换到磁盘上排序

另外, 在执行计划中有几个统计信息列 0Mem, 1Mem, Use_Mem 需要介绍一下
0Mem 指的是预计在 PGA 中排序需要的内存大小
1Mem 指的是当内存大小(PGA)不足以进行排序, 预计将数据一次交换到磁盘空间的内存大小
Used-Mem 指的是执行时实际使用的内存大小, 其中括号中的数字代表进行磁盘交换的次数, 0 代表没有进行磁盘交换

三. sort merge joins连接(排序合并连接) 优化
SQL> select /*+ leading(t3) use_merge(t4) */ * from t3, t4 where t3.id = t4.t3_id and t3.n = 1100 and t4.n = 10034;
SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------
SQL_ID bg9h60c7ak3ud, child number 0
-------------------------------------
select /*+ leading(t3) use_merge(t4) */ * from t3, t4 where t3.id = t4.t3_id and t3.n = 1100 and t4.n = 10034

Plan hash value: 3831111046
-----------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
-----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 119 | | | |
| 1 | MERGE JOIN | | 1 | 1 | 1 |00:00:00.01 | 119 | | | |
| 2 | SORT JOIN | | 1 | 1 | 1 |00:00:00.01 | 15 | 2048 | 2048 | 2048 (0)|
|* 3 | TABLE ACCESS FULL| T3 | 1 | 1 | 1 |00:00:00.01 | 15 | | | |
|* 4 | SORT JOIN | | 1 | 1 | 1 |00:00:00.01 | 104 | 2048 | 2048 | 2048 (0)|
|* 5 | TABLE ACCESS FULL| T4 | 1 | 1 | 1 |00:00:00.01 | 104 | | | |
-----------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("T3"."N"=1100)
4 - access("T3"."ID"="T4"."T3_ID")
filter("T3"."ID"="T4"."T3_ID")
5 - filter("T4"."N"=10034)

SQL> create index t4_n on t4(n);

Index created.

SQL> select /*+ leading(t3) use_merge(t4) */ * from t3, t4 where t3.id = t4.t3_id and t3.n = 1100 and t4.n = 10034;

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

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------------------------------------
SQL_ID bg9h60c7ak3ud, child number 0
-------------------------------------
select /*+ leading(t3) use_merge(t4) */ * from t3, t4 where t3.id = t4.t3_id and t3.n = 1100 and t4.n = 10034

Plan hash value: 1501658231

------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | OMem | 1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 18 | 1 | | | |
| 1 | MERGE JOIN | | 1 | 1 | 1 |00:00:00.01 | 18 | 1 | | | |
| 2 | SORT JOIN | | 1 | 1 | 1 |00:00:00.01 | 15 | 0 | 2048 | 2048 | 2048 (0)|
|* 3 | TABLE ACCESS FULL | T3 | 1 | 1 | 1 |00:00:00.01 | 15 | 0 | | | |
|* 4 | SORT JOIN | | 1 | 1 | 1 |00:00:00.01 | 3 | 1 | 2048 | 2048 | 2048 (0)|
| 5 | TABLE ACCESS BY INDEX ROWID| T4 | 1 | 1 | 1 |00:00:00.01 | 3 | 1 | | | |
|* 6 | INDEX RANGE SCAN | T4_N | 1 | 1 | 1 |00:00:00.01 | 2 | 1 | | | |
------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("T3"."N"=1100)
4 - access("T3"."ID"="T4"."T3_ID")
filter("T3"."ID"="T4"."T3_ID")
6 - access("T4"."N"=10034)

SQL> create index t3_n on t3(n);

Index created.

SQL> select /*+ leading(t3) use_merge(t4) */ * from t3, t4 where t3.id = t4.t3_id and t3.n = 1100 and t4.n = 10034;

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

PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID bg9h60c7ak3ud, child number 0
-------------------------------------
select /*+ leading(t3) use_merge(t4) */ * from t3, t4 where t3.id = t4.t3_id and t3.n = 1100 and t4.n = 10034

Plan hash value: 1827980052

------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | OMem | 1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 6 | 1 | | | |
| 1 | MERGE JOIN | | 1 | 1 | 1 |00:00:00.01 | 6 | 1 | | | |
| 2 | SORT JOIN | | 1 | 1 | 1 |00:00:00.01 | 3 | 1 | 2048 | 2048 | 2048 (0)|
| 3 | TABLE ACCESS BY INDEX ROWID| T3 | 1 | 1 | 1 |00:00:00.01 | 3 | 1 | | | |
|* 4 | INDEX RANGE SCAN | T3_N | 1 | 1 | 1 |00:00:00.01 | 2 | 1 | | | |
|* 5 | SORT JOIN | | 1 | 1 | 1 |00:00:00.01 | 3 | 0 | 2048 | 2048 | 2048 (0)|
| 6 | TABLE ACCESS BY INDEX ROWID| T4 | 1 | 1 | 1 |00:00:00.01 | 3 | 0 | | | |
|* 7 | INDEX RANGE SCAN | T4_N | 1 | 1 | 1 |00:00:00.01 | 2 | 0 | | | |
------------------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("T3"."N"=1100)
5 - access("T3"."ID"="T4"."T3_ID")
filter("T3"."ID"="T4"."T3_ID")
7 - access("T4"."N"=10034)
从上面的执行计划中可以看出, 全表扫描后最后使用的 buffer 为 119, 在一个表上建立索引使用索引范围扫描后 buffer 为 18, 在两个表上建立的索引使用索引范围扫描后 buffer 为 6.
由此可以见, 在表的谓词条件上如果有索引的话, 将会提高执行效率.此外, 由于 sort merge joins 需要先在 PGA 中进行排序,, 如果 PGA 空间不足, 就会将数据交换到磁盘上进行排序。
由于, 磁盘相对于内存来说是慢速设备,因此在磁盘上排序会比在内存上排序慢, 另外排序排序消耗的时间还需要加上数据在内存和磁盘上传输的时间,
因此尽可能减少磁盘排序的次数也就会提高执行效率, 有两种方法会减少磁盘排序:

1. 增大 PGA 的大小, 如果是 oracle 10g,需要增加参数 pga_aggregate_target 的大小,如果是 oracle 11g,则增加 memory_target 的大小
2. 减少排序的数据量, 一些不需要的字段就不要写在 select 后面

四. 小结

遇到 sql 调优时,如果执行计划显示表的连接方式是 sort merge join:
首先,看看 sql 语句是不是表的连接方式有没有可能转换为 hash join(等值连接条件)
其次,只能使用 sort merge join 时看看表的谓词条件上是不是有索引
最后,看看执行计划排序占用的内存大小是不是在磁盘上有排序, 是不是能够避免在磁盘上排序

oracle 表连接 - sort merge joins 排序合并连接的更多相关文章

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

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

  2. 排序合并连接(sort merge join)的原理

    排序合并连接(sort merge join)的原理 排序合并连接(sort merge join)的原理     排序合并连接(sort merge join)       访问次数:两张表都只会访 ...

  3. Oracle 三种连接方式 NESTED LOOP HASH JOIN SORT MERGE JOIN

    NESTED LOOP: 对于被连接的数据子集较小的情况,嵌套循环连接是个较好的选择.在嵌套循环中,内表被外表驱动,外表返回的每一行都要在内表中检索找到与它匹配的行,因此整个查询返回的结果集不能太大( ...

  4. Nested loops、Hash join、Sort merge join(三种连接类型原理、使用要点)

    nested loop 嵌套循环(原理):oracle从较小结果集(驱动表.也可以被称为outer)中读取一行,然后和较大结果集(被侦查表,也可以叫做inner)中的所有数据逐条进行比较(也是等值连接 ...

  5. Nested Loop,Sort Merge Join,Hash Join

    三种连接工作方式比较: Nested loops 工作方式是从一张表中读取数据,访问另一张表(通常是索引)来做匹配,nested loops适用的场合是当一个关联表比较小的时候,效率会更高. Merg ...

  6. Oracle表的几种连接方式

    1,排序 - - 合并连接(Sort Merge Join, SMJ) 2,嵌套循环(Nested Loops, NL) 3,哈希连接(Hash Join, HJ) Join是一种试图将两个表结合在一 ...

  7. Oracle表连接

    一个普通的语句select * from t1, t2 where t1.id = t2.id and t1.name = 'a'; 这个语句在什么情况下最高效? 表连接分类: 1. 嵌套循环连接(N ...

  8. Oracle 表连接方式分析 .

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

  9. Oracle 表三种连接方式(sql优化)

    在查看sql执行计划时,我们会发现表的连接方式有多种,本文对表的连接方式进行介绍以便更好看懂执行计划和理解sql执行原理. 一.连接方式: 嵌套循环(Nested Loops (NL)) (散列)哈希 ...

随机推荐

  1. C语言黑与白问题

    问题描述: 有A.B.C.D.E这5个人,每个人额头上都帖了一张黑或白的纸.5人对坐,每 个人都可以看到其他人额头上纸的颜色.5人相互观察后: A说:“我看见有3人额头上贴的是白纸,1人额头上贴的是黑 ...

  2. C++中的class和struct区别

    1,经过不停的改进,结构体 struct 变得原来越不像它在 C 语言中的样子了: 1,struct 在 C 语言中仅为了定义一个变量的集合,仅此而已,不能定义函数: 2,struct 在 C++ 中 ...

  3. P1067多项式输出

    这道题是2009普及组的题,仍然是一个字符串+模拟.(蒻到先不刷算法) 这道题的题干给了很多的提示,也很全面,但是当我把种种情况都考虑到了后,在写代码的过程中仍然出现了很多的错误,wa了三四次.其实导 ...

  4. 源码看React 事件机制

    对React熟悉的同学都知道,React中的事件机制并不是原生的那一套,事件没有绑定在原生DOM上,发出的事件也是对原生事件的包装.那么这一切是怎么实现的呢? 事件注册 首先还是看我们熟悉的代码 &l ...

  5. 细说C#的ReferenceEquals,Equals和==比较运算符

    C# 中有两种不同的相等:引用相等和值相等.值相等是大家普遍理解的意义上的相等:它意味着两个对象包含相同的值.例如,两个值为 2 的整数具有值相等性.引用相等意味着要比较的不是两个对象,而是两个对象引 ...

  6. java封装小实例

    封装是java语言的一个重要的特性,通过把对象的属性和操作方法封装在同一个类中,对外只提供公共方法对这些数据进行set和get,同时封装也能对方法进行封装.总之封装能够有效地隐藏内部的代码细节,从而使 ...

  7. ntsysv - 用于配置运行级别的简单接口

    总览 SYNOPSIS ntsysv [--back] [--level <levels>] 描述 DESCRIPTION ntsysv 是个用于配置运行级别服务(也可通过 chkconf ...

  8. 【原】cmdline传递参数 uboot-->kernel-->fs

    在uboot中设置bootargs环境变量,在kernel启动后cat /proc/cmdline可以看到bootargs的值. U-boot的环境变量值得注意的有两个: bootcmd 和boota ...

  9. CTF各种资源:题目、工具、资料

    目录 题目汇总 Reverse 签到题 Web Web中等难度 Crypto 基础网站 各类工具 综合 Web Payloads 逆向 Pwn 取证 题目汇总 这里收集了我做过的CTF题目 Rever ...

  10. 针对360浏览器读取不了cookie的问题

    今天学习cookie的时候发现在360和谷歌浏览器下设置cookie打开是空白的!经过一番搜索才知道在本地是访问不了cookie只能在服务器端进行访问,但是仍然可以在火狐下进行访问