此脚本不依赖统计信息。
排序合并连接一般用于非等值关联,如果两表是等值关联,我们建议使用 HASH 连接代替排序合并连接,因为 HASH 连接只需要将驱动表放入 PGA 中,而排序合并连接要么是将两个表放入 PGA 中,要么是将一个表放入 PGA 中、另外一个表走 INDEX FULL SCAN,然后回表。如果两表是等值关联并且两表比较大,这时应该走 HASH 连接而不是排序合并连接。下面脚本抓出两表等值关联但是走了排序合并连接的 SQL,同时显示离 MERGE JOIN 关键字较远的表的段大小(太大 PGA 放不下)。
select c.sql_id, c.sql_text, d.owner, d.segment_name, d.mbfrom v$sql_plan a,v$sql_plan b,v$sql c,(select owner, segment_name, sum(bytes / 1024 / 1024) mbfrom dba_segmentsgroup by owner, segment_name) dwhere a.sql_id = b.sql_idand a.child_number = b.child_numberand b.operation = 'SORT'and b.options = 'JOIN'and b.access_predicates like '%"="%'and a.parent_id = b.idand a.object_owner = 'SCOTT'and b.sql_id = c.sql_idand b.child_number = c.child_numberand a.object_owner = d.ownerand a.object_name = d.segment_nameorder by 4 desc;
我们在 Scott 账户中运行下面 SQL 并且查看执行计划。
select /*+ use_merge(e,d) */ *from emp e, dept dwhere e.deptno = d.deptno;
14 rows selected.Execution Plan----------------------------------------------------------Plan hash value: 844388907-------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|Time |-------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 14 | 812 | 6 (17)|00:00:01|| 1 | MERGE JOIN | | 14 | 812 | 6 (17)|00:00:01|| 2 | TABLE ACCESS BY INDEX ROWID| DEPT | 4 | 80 | 2 (0)|00:00:01|| 3 | INDEX FULL SCAN | PK_DEPT | 4 | | 1 (0)|00:00:01||* 4 | SORT JOIN | | 14 | 532 | 4 (25)|00:00:01|| 5 | TABLE ACCESS FULL | EMP | 14 | 532 | 3 (0)|00:00:01|-------------------------------------------------------------------------------------Predicate Information (identified by operation id):---------------------------------------------------4 - access("E"."DEPTNO"="D"."DEPTNO")filter("E"."DEPTNO"="D"."DEPTNO")
我们使用脚本将走了排序合并连接的 SQL 抓出,同时显示离 MERGE JOIN 关键字较远的表的段大小。
select c.sql_id, c.sql_text, d.owner, d.segment_name, d.mbfrom v$sql_plan a,v$sql_plan b,v$sql c,(select owner, segment_name, sum(bytes / 1024 / 1024) mbfrom dba_segmentsgroup by owner, segment_name) dwhere a.sql_id = b.sql_idand a.child_number = b.child_numberand b.operation = 'SORT'and b.options = 'JOIN'and b.access_predicates like '%"="%'and a.parent_id = b.idand a.object_owner = 'SCOTT'and b.sql_id = c.sql_idand b.child_number = c.child_numberand a.object_owner = d.ownerand a.object_name = d.segment_nameorder by 4 desc;
SQL_ID SQL_TEXT OWNER SEGMENT_NAME MB------------- ---------------------------------------- ------- ------------- --------c7gd7wn0gx4vq select /*+ use_merge(e,d) */ * from emp e, SCOTT EMP .0625dept d where e.deptno = d.deptno
