谓词中多个列等值条件,而且这些列上都有单键值的索引,oracle会合并扫描单个索引的rowid集合。html
SQL_ID 3zmhhz4cbg12f, child number 0 ------------------------------------- select /*+and_equal(a index_emp_DEPTNO IND_EMP_JOB)*/ * from scott.emp a where a.deptno=20 and a.job='SALESMAN' Plan hash value: 2438547776 ------------------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | | | 3 (100)| | |* 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 38 | 3 (0)| 00:00:01 | | 2 | AND-EQUAL | | | | | | |* 3 | INDEX RANGE SCAN | IND_EMP_JOB | 4 | | 1 (0)| 00:00:01 | |* 4 | INDEX RANGE SCAN | INDEX_EMP_DEPTNO | 5 | | 1 (0)| 00:00:01 | ------------------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter(("A"."JOB"='SALESMAN' AND "A"."DEPTNO"=20)) 3 - access("A"."JOB"='SALESMAN') 4 - access("A"."DEPTNO"=20)
经过先访问IND_EMP_JOB、INDEX_EMP_DEPTNO这两个索引后,在过滤rowid相同的在filter(("A"."JOB"='SALESMAN' AND "A"."DEPTNO"=20)) ,访问表的数据sql
index join是针对单表上的不一样索引之间的链接缓存
SQL_ID 7qdwg0qwn6tgm, child number 0 ------------------------------------- select /*+index_join(a index_emp_DEPTNO IND_EMP_JOB)*/ deptno,job from scott.emp a where a.deptno=20 and a.job='SALESMAN' Plan hash value: 2687837119 --------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 3 (100)| | |* 1 | VIEW | index$_join$_001 | 1 | 11 | 3 (34)| 00:00:01 | |* 2 | HASH JOIN | | | | | | |* 3 | INDEX RANGE SCAN| IND_EMP_JOB | 1 | 11 | 1 (0)| 00:00:01 | |* 4 | INDEX RANGE SCAN| INDEX_EMP_DEPTNO | 1 | 11 | 1 (0)| 00:00:01 | --------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter(("A"."JOB"='SALESMAN' AND "A"."DEPTNO"=20)) 2 - access(ROWID=ROWID) 3 - access("A"."JOB"='SALESMAN') 4 - access("A"."DEPTNO"=20)
经过IND_EMP_JOB取出索引信息,经过INDEX_EMP_DEPTNO取出索引信息,这两个索引信息关联,rowid=rowid,在过滤条件filter(("A"."JOB"='SALESMAN' AND "A"."DEPTNO"=20)),取出信息session
Oracle处理包含SQL时,根据视图是否可以视图合并(VIEW Merging),对应的执行计划有两种。oracle
视图合并ide
SQL语句有视图,在语句中会展开,在执行计划中极可能不会出现VIEW,可是又可能仍是存在,查看视图合并的例子oop
create or replace view emp_view as select * from scott.emp where deptno=30 select * from emp_view where job='SALESMAN' select * from table(dbms_xplan.display_cursor(null,null)) SQL_ID dwtdzmud7wdqs, child number 0 ------------------------------------- select * from emp_view where job='SALESMAN' Plan hash value: 3919104597 ---------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 2 (100)| | |* 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 38 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | IND_EMP_JENAME | 4 | | 1 (0)| 00:00:01 | ---------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("DEPTNO"=30) 2 - access("JOB"='SALESMAN')
第一步走了索引 IND_EMP_JENAME access("JOB"='SALESMAN'),第二部过滤filter("DEPTNO"=30) ,视图已经合并性能
不作视图合并优化
执行计划中出现关键字“VIEW”,定义视图中存在ROWNUMui
create or replace view emp_view as select * from scott.emp where deptno=30 and rownum<10 select * from emp_view where job='SALESMAN' select * from table(dbms_xplan.display_cursor(null,null)) SQL_ID dwtdzmud7wdqs, child number 0 ------------------------------------- select * from emp_view where job='SALESMAN' Plan hash value: 2822310472 -------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 2 (100)| | |* 1 | VIEW | EMP_VIEW | 6 | 522 | 2 (0)| 00:00:01 | |* 2 | COUNT STOPKEY | | | | | | | 3 | TABLE ACCESS BY INDEX ROWID| EMP | 6 | 228 | 2 (0)| 00:00:01 | |* 4 | INDEX RANGE SCAN | INDEX_EMP_DEPTNO | 6 | | 1 (0)| 00:00:01 | -------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("JOB"='SALESMAN') 2 - filter(ROWNUM<10) 4 - access("DEPTNO"=30)
执行计划中存在VIEW,视图为单独执行
获得一个驱动结果集
根据必定的过滤条件从上述驱动结果集中滤除不知足条件的记录
结果集中剩下的记录就会返回给最终用户或者继续参与下一个执行步骤
select /*+gather_plan_statistics*/ * from scott.emp where deptno in (select /*+no_unnest*/ deptno from scott.dept) select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS +COST')) SQL_ID 4xu8ns03jbd69, child number 0 ------------------------------------- select /*+gather_plan_statistics*/ * from scott.emp where deptno in (select /*+no_unnest*/ deptno from scott.dept) Plan hash value: 1783302997 ----------------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | Cost (%CPU)| A-Rows | A-Time | Buffers | ----------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 3 (100)| 11 |00:00:00.01 | 9 | |* 1 | FILTER | | 1 | | | 11 |00:00:00.01 | 9 | | 2 | TABLE ACCESS FULL| EMP | 1 | 14 | 3 (0)| 14 |00:00:00.01 | 6 | |* 3 | INDEX UNIQUE SCAN| PK_DEPT | 3 | 1 | 0 (0)| 2 |00:00:00.01 | 3 | ----------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter( IS NOT NULL) 3 - access("DEPTNO"=:B1) FILTER访问跟nested loop不一样,驱动表在访问被驱动表时,会对关联字段作DISTINCT,如EMP.DEPTNO作DISTINCT为3,实际运行的次数(START)为3次。不是实际行数14的次数。若是是NESTED LOOP就须要14次了 之后是NESTED LOOP的例子对比 select /*+gather_plan_statistics*/ * from scott.emp where deptno in (select deptno from scott.dept) select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS +COST')) SQL_ID bku72zf75w5rk, child number 0 ------------------------------------- select /*+gather_plan_statistics*/ * from scott.emp where deptno in (select deptno from scott.dept) Plan hash value: 3074306753 ----------------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | Cost (%CPU)| A-Rows | A-Time | Buffers | ----------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 3 (100)| 11 |00:00:00.01 | 10 | | 1 | NESTED LOOPS | | 1 | 14 | 3 (0)| 11 |00:00:00.01 | 10 | | 2 | TABLE ACCESS FULL| EMP | 1 | 14 | 3 (0)| 14 |00:00:00.01 | 6 | |* 3 | INDEX UNIQUE SCAN| PK_DEPT | 14 | 1 | 0 (0)| 11 |00:00:00.01 | 4 | ----------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("DEPTNO"="DEPTNO") 能够清晰看到被驱动表,执行次数是14次
FILTER类型的执行计划其实是一种改良的嵌套循环链接,他并不像嵌套循环链接那样,驱动结果中的有多少记录就得访问多少次被驱动表
执行计划中出现关键字“SORT”,也不必定意味着就须要排序,如SORT AGGREGATE和BUFFER SORT不必定须要排序
sys@GULL> set autotrace trace sys@GULL> select sum(sal) from scott.emp where deptno=30 2 ; 执行计划 ---------------------------------------------------------- Plan hash value: 2829802371 ------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 7 | 2 (0)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | 7 | | | | 2 | TABLE ACCESS BY INDEX ROWID| EMP | 6 | 42 | 2 (0)| 00:00:01 | |* 3 | INDEX RANGE SCAN | INDEX_EMP_DEPTNO | 6 | | 1 (0)| 00:00:01 | ------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("DEPTNO"=30) 统计信息 ---------------------------------------------------------- 1 recursive calls 0 db block gets 2 consistent gets 0 physical reads 0 redo size 535 bytes sent via SQL*Net to client 519 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed
看到sorts(memory)、sorts(disk)为0,无任何排序,可是在执行计划中能够看到sort aggregate
sys@GULL> select distinct job from scott.emp where deptno=30 order by job; 执行计划 ---------------------------------------------------------- Plan hash value: 2884078981 ------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 4 | 44 | 4 (50)| 00:00:01 | | 1 | SORT UNIQUE | | 4 | 44 | 3 (34)| 00:00:01 | | 2 | TABLE ACCESS BY INDEX ROWID| EMP | 6 | 66 | 2 (0)| 00:00:01 | |* 3 | INDEX RANGE SCAN | INDEX_EMP_DEPTNO | 6 | | 1 (0)| 00:00:01 | ------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("DEPTNO"=30) 统计信息 ---------------------------------------------------------- 1 recursive calls 0 db block gets 2 consistent gets 0 physical reads 0 redo size 605 bytes sent via SQL*Net to client 519 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 3 rows processed
查看sorts(memory)有存在排序
sys@GULL> select /*+use_merge(a b)*/ * from scott.emp a,scott.dept b where a.deptno=b.deptno; 已选择11行。 执行计划 ---------------------------------------------------------- Plan hash value: 844388907 ---------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 14 | 798 | 6 (17)| 00:00:01 | | 1 | MERGE JOIN | | 14 | 798 | 6 (17)| 00:00:01 | | 2 | TABLE ACCESS BY INDEX ROWID| DEPT | 3 | 57 | 2 (0)| 00:00:01 | | 3 | INDEX FULL SCAN | PK_DEPT | 3 | | 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("A"."DEPTNO"="B"."DEPTNO") filter("A"."DEPTNO"="B"."DEPTNO") 统计信息 ---------------------------------------------------------- 3 recursive calls 0 db block gets 16 consistent gets 1 physical reads 0 redo size 1730 bytes sent via SQL*Net to client 519 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 11 rows processed
1 sorts (memory)存在排序
sys@GULL> select job from scott.emp where deptno=30 group by job order by job; 执行计划 ---------------------------------------------------------- Plan hash value: 2097038129 ------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 4 | 44 | 3 (34)| 00:00:01 | | 1 | SORT GROUP BY | | 4 | 44 | 3 (34)| 00:00:01 | | 2 | TABLE ACCESS BY INDEX ROWID| EMP | 6 | 66 | 2 (0)| 00:00:01 | |* 3 | INDEX RANGE SCAN | INDEX_EMP_DEPTNO | 6 | | 1 (0)| 00:00:01 | ------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("DEPTNO"=30) 统计信息 ---------------------------------------------------------- 38 recursive calls 0 db block gets 51 consistent gets 0 physical reads 0 redo size 605 bytes sent via SQL*Net to client 519 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 7 sorts (memory) 0 sorts (disk) 3 rows processed
7 sorts (memory) 经过group by order by,当列为非NULL索引时,是不会排序的
sys@GULL> select job from scott.emp where deptno=30 order by job; 已选择6行。 执行计划 ---------------------------------------------------------- Plan hash value: 4045776959 ------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 6 | 66 | 3 (34)| 00:00:01 | | 1 | SORT ORDER BY | | 6 | 66 | 3 (34)| 00:00:01 | | 2 | TABLE ACCESS BY INDEX ROWID| EMP | 6 | 66 | 2 (0)| 00:00:01 | |* 3 | INDEX RANGE SCAN | INDEX_EMP_DEPTNO | 6 | | 1 (0)| 00:00:01 | ------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("DEPTNO"=30) 统计信息 ---------------------------------------------------------- 1 recursive calls 0 db block gets 2 consistent gets 0 physical reads 0 redo size 620 bytes sent via SQL*Net to client 519 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 6 rows processed
1 sorts (memory) order by子句会产生排序,执行计划的体现sort order by
sys@GULL> select * from scott.emp a,scott.dept b 2 ; 已选择42行。 执行计划 ---------------------------------------------------------- Plan hash value: 2034389985 ----------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 42 | 2394 | 9 (0)| 00:00:01 | | 1 | MERGE JOIN CARTESIAN| | 42 | 2394 | 9 (0)| 00:00:01 | | 2 | TABLE ACCESS FULL | DEPT | 3 | 57 | 3 (0)| 00:00:01 | | 3 | BUFFER SORT | | 14 | 532 | 6 (0)| 00:00:01 | | 4 | TABLE ACCESS FULL | EMP | 14 | 532 | 2 (0)| 00:00:01 | ----------------------------------------------------------------------------- 统计信息 ---------------------------------------------------------- 1 recursive calls 0 db block gets 14 consistent gets 5 physical reads 0 redo size 3449 bytes sent via SQL*Net to client 541 bytes received via SQL*Net from client 4 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 42 rows processed
buffer sort表示ORACLE会用PGA把扫描结果load进去,这样的好处是省掉相对应的缓存在SGA的开销
buffer sort可能排序,可能也不会的。
还有一种方式查看是否存在排序,在执行计划中存在
Column Projection Information (identified by operation id):
1 - (#keys=1) "JOB"[VARCHAR2,9]
#keys=1,大于1,说明排序数量为1,若是为0,没有排序
select distinct job from scott.emp where deptno=30 order by job; select * from table(dbms_xplan.display_cursor(null,null,'advanced')) SQL_ID 27vj2ut1x96m3, child number 0 ------------------------------------- select distinct job from scott.emp where deptno=30 order by job Plan hash value: 2884078981 ------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 4 (100)| | | 1 | SORT UNIQUE | | 4 | 44 | 3 (34)| 00:00:01 | | 2 | TABLE ACCESS BY INDEX ROWID| EMP | 6 | 66 | 2 (0)| 00:00:01 | |* 3 | INDEX RANGE SCAN | INDEX_EMP_DEPTNO | 6 | | 1 (0)| 00:00:01 | ------------------------------------------------------------------------------------------------- Query Block Name / Object Alias (identified by operation id): ------------------------------------------------------------- 1 - SEL$1 2 - SEL$1 / EMP@SEL$1 3 - SEL$1 / EMP@SEL$1 Outline Data ------------- /*+ BEGIN_OUTLINE_DATA IGNORE_OPTIM_EMBEDDED_HINTS OPTIMIZER_FEATURES_ENABLE('11.2.0.3') DB_VERSION('11.2.0.3') OPT_PARAM('optimizer_dynamic_sampling' 0) ALL_ROWS OUTLINE_LEAF(@"SEL$1") INDEX_RS_ASC(@"SEL$1" "EMP"@"SEL$1" ("EMP"."DEPTNO")) END_OUTLINE_DATA */ Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("DEPTNO"=30) Column Projection Information (identified by operation id): ----------------------------------------------------------- 1 - (#keys=1) "JOB"[VARCHAR2,9] 2 - "JOB"[VARCHAR2,9] 3 - "EMP".ROWID[ROWID,10]
UNION 是将两个结果集合并,去掉重复并排序。union 先作UNION ALL,在作SORT UNIQUE
select deptno from scott.emp union select deptno from scott.dept select * from table(dbms_xplan.display_cursor(null,null)) SQL_ID 9r3apuuwjtbgx, child number 0 ------------------------------------- select deptno from scott.emp union select deptno from scott.dept Plan hash value: 3432554835 -------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 4 (100)| | | 1 | SORT UNIQUE | | 17 | 51 | 4 (75)| 00:00:01 | | 2 | UNION-ALL | | | | | | | 3 | INDEX FULL SCAN| INDEX_EMP_DEPTNO | 14 | 42 | 1 (0)| 00:00:01 | | 4 | INDEX FULL SCAN| PK_DEPT | 3 | 9 | 1 (0)| 00:00:01 | --------------------------------------------------------------------------------------
union all
就是两个结果合并,不作任何处理
select deptno from scott.emp union all select deptno from scott.dept select * from table(dbms_xplan.display_cursor(null,null)) SQL_ID f42g872sqp9hd, child number 0 ------------------------------------- select deptno from scott.emp union all select deptno from scott.dept Plan hash value: 3924871334 ------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 2 (100)| | | 1 | UNION-ALL | | | | | | | 2 | INDEX FULL SCAN| INDEX_EMP_DEPTNO | 14 | 42 | 1 (0)| 00:00:01 | | 3 | INDEX FULL SCAN| PK_DEPT | 3 | 9 | 1 (0)| 00:00:01 | -------------------------------------------------------------------------------------
union all比union的性能好不少,尽可能用union all
CONCAT就是 IN-LIST扩展(IN-LIST EXPANSION) 或OR扩展(OR EXPANSION),执行计划中对应CONCATENATION。
select * from scott.emp where job in ('SALESMAN','MANAGER') select * from table(dbms_xplan.display_cursor(null,null)) SQL_ID 1sz0ywa9m6k1u, child number 0 ------------------------------------- select * from scott.emp where job in ('SALESMAN','MANAGER') Plan hash value: 3177582080 ----------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 2 (100)| | | 1 | INLIST ITERATOR | | | | | | | 2 | TABLE ACCESS BY INDEX ROWID| EMP | 7 | 266 | 2 (0)| 00:00:01 | |* 3 | INDEX RANGE SCAN | IND_EMP_JENAME | 7 | | 1 (0)| 00:00:01 | ----------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access(("JOB"='MANAGER' OR "JOB"='SALESMAN'))
在未扩展以前,采用的是INLIST ITERATOR,能够指定hint(use_concate),事件设置
alter session set events '10142 trace name context forever' alter session set events '10157 trace name context forever' select /*+use_concat*/ * from scott.emp where job in ('SALESMAN','MANAGER') select * from table(dbms_xplan.display_cursor(null,null)) SQL_ID 6u1d9uaruw10d, child number 0 ------------------------------------- select /*+use_concat*/ * from scott.emp where job in ('SALESMAN','MANAGER') Plan hash value: 1170295018 ----------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 4 (100)| | | 1 | CONCATENATION | | | | | | | 2 | TABLE ACCESS BY INDEX ROWID| EMP | 3 | 114 | 2 (0)| 00:00:01 | |* 3 | INDEX RANGE SCAN | IND_EMP_JENAME | 3 | | 1 (0)| 00:00:01 | | 4 | TABLE ACCESS BY INDEX ROWID| EMP | 4 | 152 | 2 (0)| 00:00:01 | |* 5 | INDEX RANGE SCAN | IND_EMP_JENAME | 4 | | 1 (0)| 00:00:01 | ----------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("JOB"='MANAGER') 5 - access("JOB"='SALESMAN')
一般INLIST ITERATOR比CONCATENATION性能好。
内容来源:《基于oracle的SQL优化》