12c在优化器方面引入了不少的新特性,其中一个就是Adaptive Plans
。
在特定的条件下优化器会使用这个新特性,比如sql语句包含了表关联、复杂的谓词条件导致很难精准的估算cardinality,Adaptive Plans
使优化器可以等到真正在执行sql语句的时候才决定采用哪种执行计划。
优化器会优先选择一个默认执行计划,同时搭配一个_statistics collectors_,这样就能发现实际的cardinality和估算的cardinality之间是否有比较大的区别,如果有明显区别,那么Adaptive Plans
这个新特性就能自动的去选择更优的执行计划。
Adaptive Join Methods 优化器能动态调整连接方法,为整个plan的多个部分预先设定多个子plan。
例如上图,优化器默认选择的计划是PRODUCTS表走索引扫描,然后与ORDERS全表扫描的结果集做嵌套循环,但同时优化器也提供了另一种关联方式,就是两个表都走全表扫描,然后做hash join,然后具体执行的时候该选择那一种就是一个比较复杂的判断了。
sql语句在开始执行之前,statistics collectors 会收集必要的信息来提供给下一步的执行计划,而具体要收集哪些信息则是有优化器来决定。首先优化器会计算出一个特定的拐点 ,作为各个子plan的判断依据。比如假设ORDERS表返回的结果集小于10,这个时候nested loop的COST更优,而结果集大于10时,hash join的COST更好,那么优化器就计算出10这个值作为拐点 。优化器之后就会让statistics collectors 去统计返回值,如果扫描到10行以上,则从NESTED LOOPS切换到HASH JOIN,否则则维持NESTED LOOPS。statistics collectors 主要的作用就是在ORDERS表做全表扫描的过程中进行监控和缓存行,优化器根据statistics collectors 得到的信息来决定使用哪个执行计划。
先建两张表
1 2 3 4 5 6 7 8 9 10 11 12 13 drop table t1 purge; drop table t2 purge; create table t1 as select object_id ,object_name,owner from dba_objects where object_id is not null; alter table t1 add primary key(object_id); create index idx_t1 on t1(object_name); create table t2 as select object_id ,object_name,owner from dba_objects where object_id is not null; alter table t2 add primary key(object_id); EXEC DBMS_STATS.gather_table_stats(USER, 'T1'); EXEC DBMS_STATS.gather_table_stats(USER, 'T2');
做一个两表关联的sql,看执行计划会是什么样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 select t1.owner ,t2.owner from t1,t2 where t1.object_id=t2.object_id and t1.object_name='TS$'; ------------------------------------------------------------------------------------------- | Id | Operation | Name | E-Rows |E-Bytes| Cost (%CPU)| ------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 5 (100)| | 1 | NESTED LOOPS | | 1 | 55 | 5 (0)| | 2 | NESTED LOOPS | | 1 | 55 | 5 (0)| | 3 | TABLE ACCESS BY INDEX ROWID BATCHED| T1 | 1 | 45 | 4 (0)| |* 4 | INDEX RANGE SCAN | IDX_T1 | 1 | | 3 (0)| |* 5 | INDEX UNIQUE SCAN | SYS_C007364 | 1 | | 0 (0)| | 6 | TABLE ACCESS BY INDEX ROWID | T2 | 1 | 10 | 1 (0)| ------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 4 - access("T1"."OBJECT_NAME"='TS$') 5 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID") - this is an adaptive plan >>>>====表示自适应执行计划
可以通过DBMS_XPLAN.DISPLAY_CURSOR的新参数看到一个自适应计划的每一个步骤
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 SYS@ora12c> SELECT * FROM TABLE(DBMS_XPLAN.display_cursor(format => 'adaptive')); Plan hash value: 970098525 --------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 5 (100)| | |- * 1 | HASH JOIN | | 1 | 55 | 5 (0)| 00:00:01 | | 2 | NESTED LOOPS | | 1 | 55 | 5 (0)| 00:00:01 | | 3 | NESTED LOOPS | | 1 | 55 | 5 (0)| 00:00:01 | |- 4 | STATISTICS COLLECTOR | | | | | | | 5 | TABLE ACCESS BY INDEX ROWID BATCHED| T1 | 1 | 45 | 4 (0)| 00:00:01 | | * 6 | INDEX RANGE SCAN | IDX_T1 | 1 | | 3 (0)| 00:00:01 | | * 7 | INDEX UNIQUE SCAN | SYS_C007364 | 1 | | 0 (0)| | | 8 | TABLE ACCESS BY INDEX ROWID | T2 | 1 | 10 | 1 (0)| 00:00:01 | |- 9 | TABLE ACCESS FULL | T2 | 1 | 10 | 1 (0)| 00:00:01 | --------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID") 6 - access("T1"."OBJECT_NAME"='TS$') 7 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID") Note ----- - this is an adaptive plan (rows marked '-' are inactive)
每个ID前面带有’-‘符号的表示这个操作并没有被执行计划所采用。
为了得到真实的执行计划,通常建议采用/*+ GATHER_PLAN_STATISTICS */
hint的方式来执行语句,然后通过allstats last的方式来查询结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 select /*+ GATHER_PLAN_STATISTICS */t1.owner ,t2.owner from t1,t2 where t1.object_id=t2.object_id and t1.object_name='TS$'; SYS@ora12c> SELECT * FROM TABLE(DBMS_XPLAN.display_cursor(format => 'adaptive allstats last')); PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- SQL_ID 49n5jxqsqksss, child number 0 ------------------------------------- select /*+ GATHER_PLAN_STATISTICS */t1.owner ,t2.owner from t1,t2 where t1.object_id=t2.object_id and t1.object_name='TS$' Plan hash value: 970098525 ------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | ------------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 7 | |- * 1 | HASH JOIN | | 1 | 1 | 1 |00:00:00.01 | 7 | | 2 | NESTED LOOPS | | 1 | 1 | 1 |00:00:00.01 | 7 | | 3 | NESTED LOOPS | | 1 | 1 | 1 |00:00:00.01 | 6 | |- 4 | STATISTICS COLLECTOR | | 1 | | 1 |00:00:00.01 | 4 | | 5 | TABLE ACCESS BY INDEX ROWID BATCHED| T1 | 1 | 1 | 1 |00:00:00.01 | 4 | | * 6 | INDEX RANGE SCAN | IDX_T1 | 1 | 1 | 1 |00:00:00.01 | 3 | | * 7 | INDEX UNIQUE SCAN | SYS_C007364 | 1 | 1 | 1 |00:00:00.01 | 2 | | 8 | TABLE ACCESS BY INDEX ROWID | T2 | 1 | 1 | 1 |00:00:00.01 | 1 | |- 9 | TABLE ACCESS FULL | T2 | 0 | 1 | 0 |00:00:00.01 | 0 | ------------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID") 6 - access("T1"."OBJECT_NAME"='TS$') 7 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID") Note ----- - this is an adaptive plan (rows marked '-' are inactive) 33 rows selected.
现在对表中的数据做一些处理,看执行计划是否能自适应变更
1 2 3 4 5 6 7 8 9 update t1 set object_name='TS$' where rownum>60000; commit; 59999 rows updated. SYS@ora12c> Commit complete.
现在T1表的查询条件满足的值不再是一条,而是60000条,这里我并没有重新收集统计信息 ,看最新的执行计划会是怎样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 SQL_ID 49n5jxqsqksss, child number 0 ------------------------------------- select /*+ GATHER_PLAN_STATISTICS */t1.owner ,t2.owner from t1,t2 where t1.object_id=t2.object_id and t1.object_name='TS$' Plan hash value: 970098525 ------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | ------------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 59999 |00:00:00.15 | 62438 | |- * 1 | HASH JOIN | | 1 | 1 | 59999 |00:00:00.15 | 62438 | | 2 | NESTED LOOPS | | 1 | 1 | 59999 |00:00:00.13 | 62438 | | 3 | NESTED LOOPS | | 1 | 1 | 59999 |00:00:00.13 | 2439 | |- 4 | STATISTICS COLLECTOR | | 1 | | 59999 |00:00:00.08 | 1094 | | 5 | TABLE ACCESS BY INDEX ROWID BATCHED| T1 | 1 | 1 | 59999 |00:00:00.03 | 1094 | | * 6 | INDEX RANGE SCAN | IDX_T1 | 1 | 1 | 59999 |00:00:00.06 | 565 | | * 7 | INDEX UNIQUE SCAN | SYS_C007364 | 59999 | 1 | 59999 |00:00:00.02 | 1345 | | 8 | TABLE ACCESS BY INDEX ROWID | T2 | 59999 | 1 | 59999 |00:00:00.03 | 59999 | |- 9 | TABLE ACCESS FULL | T2 | 0 | 1 | 0 |00:00:00.01 | 0 | ------------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID") 6 - access("T1"."OBJECT_NAME"='TS$') 7 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID") Note ----- - this is an adaptive plan (rows marked '-' are inactive)
可以看到重新执行以后,执行计划并未有什么改变,这是由于最终的执行计划是由第一次运行时所决定了
清除shared pool,强制硬解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 SYS@ora12c> alter system flush shared_pool 2 ; System altered. SYS@ora12c> @1 SYS@ora12c> SELECT * FROM TABLE(DBMS_XPLAN.display_cursor(format => 'allstats last adaptive')); PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- SQL_ID 49n5jxqsqksss, child number 0 ------------------------------------- select /*+ GATHER_PLAN_STATISTICS */t1.owner ,t2.owner from t1,t2 where t1.object_id=t2.object_id and t1.object_name='TS$' Plan hash value: 925498821 ---------------------------------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem | ---------------------------------------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 59999 |00:00:00.08 | 1268 | | | | | * 1 | HASH JOIN | | 1 | 1 | 59999 |00:00:00.08 | 1268 | 4515K| 2259K| 4429K (0)| |- 2 | NESTED LOOPS | | 1 | 1 | 59999 |00:00:00.04 | 635 | | | | |- 3 | NESTED LOOPS | | 1 | 1 | 59999 |00:00:00.04 | 635 | | | | |- 4 | STATISTICS COLLECTOR | | 1 | | 59999 |00:00:00.03 | 635 | | | | | 5 | TABLE ACCESS BY INDEX ROWID BATCHED| T1 | 1 | 1 | 59999 |00:00:00.02 | 635 | | | | | * 6 | INDEX RANGE SCAN | IDX_T1 | 1 | 1 | 59999 |00:00:00.01 | 224 | | | | |- * 7 | INDEX UNIQUE SCAN | SYS_C007364 | 0 | 1 | 0 |00:00:00.01 | 0 | | | | |- 8 | TABLE ACCESS BY INDEX ROWID | T2 | 0 | 1 | 0 |00:00:00.01 | 0 | | | | | 9 | TABLE ACCESS FULL | T2 | 1 | 1 | 72661 |00:00:00.01 | 633 | | | | ---------------------------------------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID") 6 - access("T1"."OBJECT_NAME"='TS$') 7 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID") Note ----- - this is an adaptive plan (rows marked '-' are inactive) 33 rows selected.
最终的结果显示完整的执行计划并没有区别,由于硬解析过程中_STATISTICS COLLECTOR_重新收集了数据,所以根据新的统计信息优化器选择了更优的子计划,用hash join代替了之前的nested loop。
Adaptive Parallel Distribution Method 当一个sql语句并发执行时,例如排序、分组或者关联时需要数据分布在多个不同的并发子进程中。优化器选择哪种分布操作取决于有多少并发子进程,多少行数据。如果优化器无法准确的估算出有多少数据,那就不能得到最优的分布操作。
由于新的自适应分布操作特性(HYBRID HASH ),优化器可以直到在最终执行sql时才决定采用哪种分布操作,同理会先采用_STATISTICS COLLECTOR_收集一波需要缓存的数据信息,根据得到的rows信息来决定分布操作。同样会计算出一个拐点 ,根据收集到的值与拐点 最比较,最终确定分布操作是用HASH还是广播。跟Adaptive JOIN METHOD
只影响第一次执行不同的是,Adaptive Parallel Distribution Method
影响sql的每一次执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 EXEC DBMS_STATS.gather_table_stats(USER, 'T1'); EXEC DBMS_STATS.gather_table_stats(USER, 'T2'); SYS@ora12c> alter system flush shared_pool; System altered. select /*+ GATHER_PLAN_STATISTICS PARALLEL(8)*/t1.owner ,t2.owner from t1,t2 where t1.object_id=t2.object_id and t1.object_name='TS$'; SYS@ora12c> SELECT * FROM TABLE(DBMS_XPLAN.display_cursor(format => 'allstats last adaptive')); SQL_ID gjm4v4t32wamn, child number 0 ------------------------------------- select /*+ GATHER_PLAN_STATISTICS PARALLEL(8)*/t1.owner ,t2.owner from t1,t2 where t1.object_id=t2.object_id and t1.object_name='TS$' Plan hash value: 435755347 -------------------------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem | Used-Tmp| -------------------------------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 59999 |00:00:00.44 | 40 | | | | | | 1 | PX COORDINATOR | | 1 | | 59999 |00:00:00.44 | 40 | 73728 | 73728 | | | | 2 | PX SEND QC (RANDOM) | :TQ10002 | 0 | 60537 | 0 |00:00:00.01 | 0 | | | | | |* 3 | HASH JOIN BUFFERED | | 0 | 60537 | 0 |00:00:00.01 | 0 | 7106K| 3091K| 4581K (1)| 1024 | | 4 | PX RECEIVE | | 0 | 72661 | 0 |00:00:00.01 | 0 | | | | | | 5 | PX SEND HYBRID HASH | :TQ10000 | 0 | 72661 | 0 |00:00:00.01 | 0 | | | | | >>>>====新的HYBRID HASH分布操作 | 6 | STATISTICS COLLECTOR | | 0 | | 0 |00:00:00.01 | 0 | | | | | | 7 | PX BLOCK ITERATOR | | 0 | 72661 | 0 |00:00:00.01 | 0 | | | | | |* 8 | TABLE ACCESS FULL | T2 | 0 | 72661 | 0 |00:00:00.01 | 0 | | | | | | 9 | PX RECEIVE | | 0 | 60537 | 0 |00:00:00.01 | 0 | | | | | | 10 | PX SEND HYBRID HASH | :TQ10001 | 0 | 60537 | 0 |00:00:00.01 | 0 | | | | | >>>>====新的HYBRID HASH分布操作 | 11 | PX BLOCK ITERATOR | | 0 | 60537 | 0 |00:00:00.01 | 0 | | | | | |* 12 | TABLE ACCESS FULL | T1 | 0 | 60537 | 0 |00:00:00.01 | 0 | | | | | -------------------------------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID") 8 - access(:Z>=:Z AND :Z>=:Z) 12 - access(:Z>=:Z AND :Z>=:Z) filter("T1"."OBJECT_NAME"='TS$') Note ----- - Degree of Parallelism is 8 because of hint
Adaptive Bitmap Index Pruning 当优化器生成一个星型转换的执行计划时,必须要选择正确的位图索引结合方式,用以减少访问相关的ROWIDS。如果访问了太多索引,而部分索引又不能很好的过滤数据,那么减少访问的索引量无疑能增加执行效率。Adaptive Bitmap Index Pruning
这个特性就是用来自动做这些索引的裁剪工作。
先构建一个星型转换的场景
新建一张事实表和三张维度表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 create table DIM1 (dim1_id number,DIM1_COD varchar2(10),DIM1_TXT varchar2(10)); insert into DIM1 values(1,'One','One'); insert into DIM1 values(2,'Two','Two'); insert into DIM1 values(3,'Three','Three'); insert into DIM1 values(4,'Four','Four'); insert into DIM1 values(5,'Five','Five'); insert into DIM1 values(6,'Six','Six'); insert into DIM1 values(7,'Seven','Seven'); insert into DIM1 values(8,'Eight','Eight'); insert into DIM1 values(9,'Nine','Nine'); insert into DIM1 values(10,'Ten','Ten'); commit; create table DIM2 as select DIM1_ID DIM2_ID,DIM1_COD DIM2_COD,DIM1_TXT DIM2_TXT from DIM1 where rownum>=10; create table DIM3 as select DIM1_ID DIM3_ID,DIM1_COD DIM3_COD,DIM1_TXT DIM3_TXT from DIM1 where rownum>=10; create table FACT as select rownum FACT_ID,DIM1_ID,DIM2_ID,DIM3_ID,mod(rownum,1000)/10 FACT_MESURE from DIM1,DIM2,DIM3,(select * from dual connect by level>=1000); alter table DIM1 add constraint DIM1PK primary key(DIM1_ID); alter table FACT add constraint DIM1FK foreign key (DIM1_ID) references DIM1; create index DIM1BX on FACT(DIM1_ID); alter table DIM2 add constraint DIM2PK primary key(DIM2_ID); alter table FACT add constraint DIM2FK foreign key (DIM2_ID) references DIM2; create index DIM2BX on FACT(DIM2_ID); alter table DIM3 add constraint DIM3PK primary key(DIM3_ID); alter table FACT add constraint DIM3FK foreign key (DIM3_ID) references DIM3; create index DIM3BX on FACT(DIM3_ID); SYS@ora12c> alter session set star_transformation_enabled=true; Session altered.
收集相关统计信息
1 2 3 4 exec DBMS_STATS.gather_table_stats(USER, 'DIM1'); exec DBMS_STATS.gather_table_stats(USER, 'DIM2'); exec DBMS_STATS.gather_table_stats(USER, 'DIM3'); exec DBMS_STATS.gather_table_stats(USER, 'FACT');
当维度表的数据很少时,如果索引的选择性又很好,那么星型转换的效率很高。如下结果:首先对3张维度表进行了扫描(每张表都应用了对应的谓词条件),对三个结果集进行了笛卡尔积关联得到满足所有谓词的数据,得到的结果集包含用于访问fact表的维度键以及得到最后结果所需要的相关信息。对于每一个维度键扫描bitmap index得到相应的行,这些bitmaps随后合并到一起,做完and运算后转换成ROWID后找到对应fact表。这里statistics collectors的目的主要是提供缓存的数据信息,来确定之后是用nested loop还是其他。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 SQL_ID 4dk2udqhwz501, child number 0 ------------------------------------- select /*+ GATHER_PLAN_STATISTICS */* from FACT join DIM1 using(DIM1_ID) join DIM2 using(DIM2_ID) join DIM3 using(DIM3_ID) where DIM1_COD='One' and DIM2_COD='One' and DIM3_COD='One' Plan hash value: 246786650 ----------------------------------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem | ----------------------------------------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 1000 |00:00:00.04 | 1636 | | | | | * 1 | HASH JOIN | | 1 | 1000 | 1000 |00:00:00.04 | 1636 | 1123K| 1123K| 414K (0)| | 2 | MERGE JOIN CARTESIAN | | 1 | 1 | 1 |00:00:00.01 | 21 | | | | | 3 | MERGE JOIN CARTESIAN | | 1 | 1 | 1 |00:00:00.01 | 12 | | | | | * 4 | TABLE ACCESS FULL | DIM1 | 1 | 1 | 1 |00:00:00.01 | 3 | | | | | 5 | BUFFER SORT | | 1 | 1 | 1 |00:00:00.01 | 9 | 2048 | 2048 | 2048 (0)| | * 6 | TABLE ACCESS FULL | DIM2 | 1 | 1 | 1 |00:00:00.01 | 9 | | | | | 7 | BUFFER SORT | | 1 | 1 | 1 |00:00:00.01 | 9 | 2048 | 2048 | 2048 (0)| | * 8 | TABLE ACCESS FULL | DIM3 | 1 | 1 | 1 |00:00:00.01 | 9 | | | | | 9 | VIEW | VW_ST_A500F760 | 1 | 1000 | 1000 |00:00:00.04 | 1615 | | | | | 10 | NESTED LOOPS | | 1 | 1000 | 1000 |00:00:00.04 | 1615 | | | | | 11 | BITMAP CONVERSION TO ROWIDS | | 1 | 1000 | 1000 |00:00:00.04 | 615 | | | | | 12 | BITMAP AND | | 1 | | 1 |00:00:00.04 | 615 | | | | | 13 | BITMAP MERGE | | 1 | | 1 |00:00:00.01 | 201 | 1024K| 512K|20480 (0)| | 14 | BITMAP KEY ITERATION | | 1 | | 1 |00:00:00.01 | 201 | | | | | * 15 | TABLE ACCESS FULL | DIM1 | 1 | 1 | 1 |00:00:00.01 | 3 | | | | | 16 | BITMAP CONVERSION FROM ROWIDS | | 1 | | 1 |00:00:00.01 | 198 | | | | | * 17 | INDEX RANGE SCAN | DIM1BX | 1 | | 100K|00:00:00.07 | 198 | | | | |- 18 | STATISTICS COLLECTOR | | 1 | | 2 |00:00:00.01 | 207 | | | | | 19 | BITMAP MERGE | | 1 | | 2 |00:00:00.01 | 207 | 1024K| 512K|47104 (0)| | 20 | BITMAP KEY ITERATION | | 1 | | 2 |00:00:00.01 | 207 | | | | | * 21 | TABLE ACCESS FULL | DIM2 | 1 | 1 | 1 |00:00:00.01 | 9 | | | | | 22 | BITMAP CONVERSION FROM ROWIDS| | 1 | | 2 |00:00:00.01 | 198 | | | | | * 23 | INDEX RANGE SCAN | DIM2BX | 1 | | 100K|00:00:00.06 | 198 | | | | |- 24 | STATISTICS COLLECTOR | | 1 | | 5 |00:00:00.01 | 207 | | | | | 25 | BITMAP MERGE | | 1 | | 5 |00:00:00.01 | 207 | 1024K| 512K| 146K (0)| | 26 | BITMAP KEY ITERATION | | 1 | | 5 |00:00:00.01 | 207 | | | | | * 27 | TABLE ACCESS FULL | DIM3 | 1 | 1 | 1 |00:00:00.01 | 9 | | | | | 28 | BITMAP CONVERSION FROM ROWIDS| | 1 | | 5 |00:00:00.01 | 198 | | | | | * 29 | INDEX RANGE SCAN | DIM3BX | 1 | | 100K|00:00:00.06 | 198 | | | | | 30 | TABLE ACCESS BY USER ROWID | FACT | 1000 | 1 | 1000 |00:00:00.01 | 1000 | | | | ----------------------------------------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("ITEM_3"="DIM3"."DIM3_ID" AND "ITEM_2"="DIM2"."DIM2_ID" AND "ITEM_1"="DIM1"."DIM1_ID") 4 - filter("DIM1"."DIM1_COD"='One') 6 - filter("DIM2"."DIM2_COD"='One') 8 - filter("DIM3"."DIM3_COD"='One') 15 - filter("DIM1"."DIM1_COD"='One') 17 - access("FACT"."DIM1_ID"="DIM1"."DIM1_ID") 21 - filter("DIM2"."DIM2_COD"='One') 23 - access("FACT"."DIM2_ID"="DIM2"."DIM2_ID") 27 - filter("DIM3"."DIM3_COD"='One') 29 - access("FACT"."DIM3_ID"="DIM3"."DIM3_ID") Note ----- - star transformation used for this statement - this is an adaptive plan (rows marked '-' are inactive)
假设表记录估算错误,导致维度表不能很好的过滤掉数据,fact表中大部分的数据都满足维度表的要求,那么优化器会直接先忽略掉那个维度表的条件,先做其他纬度表的bitmap合并操作,等到最后再与这个表做必要的关联。
现在对数据做下处理,更改一下DIM3表的数据,使其所有的数据都满足谓词条件
1 2 3 4 5 6 7 8 9 update DIM3 set DIM3_COD='One'; 10 rows updated. SYS@ora12c> SYS@ora12c> commit; Commit complete.
重新查看新的执行计划
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 SYS@ora12c> alter system flush shared_pool; System altered. SYS@ora12c> @1 SYS@ora12c> SELECT * FROM TABLE(DBMS_XPLAN.display_cursor(format => 'allstats last adaptive')); PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- SQL_ID 4dk2udqhwz501, child number 0 ------------------------------------- select /*+ GATHER_PLAN_STATISTICS */* from FACT join DIM1 using(DIM1_ID) join DIM2 using(DIM2_ID) join DIM3 using(DIM3_ID) where DIM1_COD='One' and DIM2_COD='One' and DIM3_COD='One' Plan hash value: 788957811 -------------------------------------------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | OMem | 1Mem | Used-Mem | -------------------------------------------------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 10000 |00:00:00.18 | 3457 | 3 | | | | | * 1 | HASH JOIN | | 1 | 1000 | 10000 |00:00:00.18 | 3457 | 3 | 1123K| 1123K| 921K (0)| | 2 | MERGE JOIN CARTESIAN | | 1 | 1 | 10 |00:00:00.01 | 21 | 0 | | | | | 3 | MERGE JOIN CARTESIAN | | 1 | 1 | 1 |00:00:00.01 | 12 | 0 | | | | | * 4 | TABLE ACCESS FULL | DIM1 | 1 | 1 | 1 |00:00:00.01 | 3 | 0 | | | | | 5 | BUFFER SORT | | 1 | 1 | 1 |00:00:00.01 | 9 | 0 | 2048 | 2048 | 2048 (0)| | * 6 | TABLE ACCESS FULL | DIM2 | 1 | 1 | 1 |00:00:00.01 | 9 | 0 | | | | | 7 | BUFFER SORT | | 1 | 1 | 10 |00:00:00.01 | 9 | 0 | 2048 | 2048 | 2048 (0)| | * 8 | TABLE ACCESS FULL | DIM3 | 1 | 1 | 10 |00:00:00.01 | 9 | 0 | | | | | 9 | VIEW | VW_ST_A500F760 | 1 | 1000 | 10000 |00:00:00.18 | 3436 | 3 | | | | | 10 | NESTED LOOPS | | 1 | 1000 | 10000 |00:00:00.18 | 3436 | 3 | | | | | 11 | BITMAP CONVERSION TO ROWIDS | | 1 | 1000 | 10000 |00:00:00.15 | 2392 | 3 | | | | | 12 | BITMAP AND | | 1 | | 1 |00:00:00.15 | 2392 | 3 | | | | | 13 | BITMAP MERGE | | 1 | | 1 |00:00:00.01 | 201 | 0 | 1024K| 512K|20480 (0)| | 14 | BITMAP KEY ITERATION | | 1 | | 1 |00:00:00.01 | 201 | 0 | | | | | * 15 | TABLE ACCESS FULL | DIM1 | 1 | 1 | 1 |00:00:00.01 | 3 | 0 | | | | | 16 | BITMAP CONVERSION FROM ROWIDS | | 1 | | 1 |00:00:00.01 | 198 | 0 | | | | | * 17 | INDEX RANGE SCAN | DIM1BX | 1 | | 100K|00:00:00.14 | 198 | 0 | | | | |- 18 | STATISTICS COLLECTOR | | 1 | | 2 |00:00:00.01 | 207 | 0 | | | | | 19 | BITMAP MERGE | | 1 | | 2 |00:00:00.01 | 207 | 0 | 1024K| 512K|47104 (0)| | 20 | BITMAP KEY ITERATION | | 1 | | 2 |00:00:00.01 | 207 | 0 | | | | | * 21 | TABLE ACCESS FULL | DIM2 | 1 | 1 | 1 |00:00:00.01 | 9 | 0 | | | | | 22 | BITMAP CONVERSION FROM ROWIDS| | 1 | | 2 |00:00:00.01 | 198 | 0 | | | | | * 23 | INDEX RANGE SCAN | DIM2BX | 1 | | 100K|00:00:00.12 | 198 | 0 | | | | |- 24 | STATISTICS COLLECTOR | | 1 | | 5 |00:00:00.12 | 1984 | 3 | | | | >>>>==== |- 25 | BITMAP MERGE | | 1 | | 5 |00:00:00.12 | 1984 | 3 | 1024K| 512K| 1454K (0)| >>>>==== |- 26 | BITMAP KEY ITERATION | | 1 | | 50 |00:00:00.01 | 1984 | 3 | | | | >>>>==== |- * 27 | TABLE ACCESS FULL | DIM3 | 1 | 1 | 10 |00:00:00.01 | 9 | 0 | | | | >>>>==== |- 28 | BITMAP CONVERSION FROM ROWIDS| | 10 | | 50 |00:00:00.03 | 1975 | 3 | | | | >>>>==== |- * 29 | INDEX RANGE SCAN | DIM3BX | 10 | | 1000K|00:00:01.65 | 1975 | 3 | | | | >>>>==== | 30 | TABLE ACCESS BY USER ROWID | FACT | 10000 | 1 | 10000 |00:00:00.01 | 1044 | 0 | | | | -------------------------------------------------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("ITEM_3"="DIM3"."DIM3_ID" AND "ITEM_2"="DIM2"."DIM2_ID" AND "ITEM_1"="DIM1"."DIM1_ID") 4 - filter("DIM1"."DIM1_COD"='One') 6 - filter("DIM2"."DIM2_COD"='One') 8 - filter("DIM3"."DIM3_COD"='One') 15 - filter("DIM1"."DIM1_COD"='One') 17 - access("FACT"."DIM1_ID"="DIM1"."DIM1_ID") 21 - filter("DIM2"."DIM2_COD"='One') 23 - access("FACT"."DIM2_ID"="DIM2"."DIM2_ID") 27 - filter("DIM3"."DIM3_COD"='One') 29 - access("FACT"."DIM3_ID"="DIM3"."DIM3_ID") Note ----- - star transformation used for this statement - this is an adaptive plan (rows marked '-' are inactive)
可以看到我标注的部分全部都被优化器忽略掉了,并没有走DIM3表的位图扫描、合并等操作,只是最后过滤的时候用到了DIM3进行了hash join。