Dynamic Sampling started from ORACLE 9i, and Renamed to dynamic statistics after ORACLE 12C. Basically, dynamic statistics is just for ORACLE optimizer to build the best execution plan, Before ORACLE 12C, the SQL parsing time would do the dynamic statistics if the existing statistics in dictionary is not sufficiency.
In 11G, ORACLE provide the extended statistics to address the multiplex columns filter which no standard statistics to tell the selectivity of query, however, such extended statistics as to be built manually. From 12C, ORACLE dynamic statistics would automatically add in the columns composition into the dictionary to let the gathering statistics auto generate the statistics as extended statistics to make optimizer more accuracy to computer the combined columns statistics even cross the tables if query has long running time with tables join.
Dynamic statistics only be triggered when no enough statistics in dictionary to let optimizer computer the selective when it is COST base, and with estimated “big” size of segment, and it should on parallel mode. dynamic statistics only be used for complex query which optimizer has to use the selective to compute the COST of access path and parallel degree.
Rule one : Parallel Mode + Estimated Fetching size + No enough statistics (either not existing or stale) ==> Dynamic statistics
Rule two: In 11g, parallel degree under auto drop is base on the segment level scan, and not directly related with statistics, but in 12c, degree compute will be base on the statistics, and dynamic statistics would be triggered if statistics is idle.
Rule three: When column histogram existing, optimizer will respect to the histogram even dynamic statistics been triggered.
Cases study list below is check on:
- Parallel policy =’AUTO’ VS force parallel via parallel hint
- Has statistics VS no statistics at all
- Has column histogram VS no histogram but with statistics.
All the execution plan was from 11GR2, for 12C, most cases are same, but parallel degree will be diffs. This paper only study when the dynamic statistics would be triggered and how it would impact the execution plan.
Case study only on one table,
- column “OWNER” has no-even values, “USER1” and “USER2” has 80% records, and statistics been gathered;
- Switched “USER2” to “USER3” w/o statistics updated.
- No. of Records owner=’USER2′ is zero. but statistics showed 80% of total records.
- No. of Records owner=’USER3′ is 80% of Total records, ut statistics showed zero of records.
- There is histogram statistics on “OWNER” column.
Case 1: Add parallel hint on the query with “OWNER” column and one function on the column to trigger the dynamic statistics base rule one.
SQL> select /*+ parallel(32) */ avg(last_ddl_time-sysdate), avg(created-sysdate) from test2 where owner = 'USER2' and substr(object_type,1,3)='TAB';
AVG(LAST_DDL_TIME-SYSDATE) AVG(CREATED-SYSDATE)
-------------------------- --------------------
Elapsed: 00:00:00.68
Execution Plan
----------------------------------------------------------
Plan hash value: 2571823673
------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | TQ |IN-OUT| PQ Distrib |
------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 35 | 117 (6)| 00:00:01 | | | |
| 1 | SORT AGGREGATE | | 1 | 35 | | | | | |
| 2 | PX COORDINATOR | | | | | | | | |
| 3 | PX SEND QC (RANDOM) | :TQ10000 | 1 | 35 | | | Q1,00 | P->S | QC (RAND) |
| 4 | SORT AGGREGATE | | 1 | 35 | | | Q1,00 | PCWP | |
| 5 | PX BLOCK ITERATOR | | 1800 | 63000 | 117 (6)| 00:00:01 | Q1,00 | PCWC | |
|* 6 | TABLE ACCESS STORAGE FULL| TEST2 | 1800 | 63000 | 117 (6)| 00:00:01 | Q1,00 | PCWP | |
------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
6 - storage("OWNER"='USER2' AND SUBSTR("OBJECT_TYPE",1,3)='TAB')
filter("OWNER"='USER2' AND SUBSTR("OBJECT_TYPE",1,3)='TAB')
Note
-----
- dynamic sampling used for this statement (level=5)
- Degree of Parallelism is 32 because of hint
Statistics
----------------------------------------------------------
176 recursive calls
0 db block gets
200701 consistent gets
171375 physical reads
3817584 redo size
433 bytes sent via SQL*Net to client
360 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
7 sorts (memory)
0 sorts (disk)
1 rows processed
It ran with dynamic statistics due to force parallel and statistics histogram showed fetching size is Big.
SQL> select /*+ parallel(32) */ avg(last_ddl_time-sysdate), avg(created-sysdate) from test2 where owner = 'USER3' and substr(object_type,1,3)='TAB';
AVG(LAST_DDL_TIME-SYSDATE) AVG(CREATED-SYSDATE)
-------------------------- --------------------
-577.32249 -870.46987
Elapsed: 00:00:16.08
Execution Plan
----------------------------------------------------------
Plan hash value: 1204039106
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 35 | 24 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 35 | | |
|* 2 | TABLE ACCESS BY INDEX ROWID| TEST2 | 955K| 31M| 24 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | INDEX1_TEST2 | 889 | | 5 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(SUBSTR("OBJECT_TYPE",1,3)='TAB')
3 - access("OWNER"='USER3')
Note
-----
- dynamic sampling used for this statement (level=5)
- Degree of Parallelism is 1 because of hint
Statistics
----------------------------------------------------------
4 recursive calls
0 db block gets
86487 consistent gets
9845 physical reads
600732 redo size
475 bytes sent via SQL*Net to client
360 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
Even USER3 has 80% of records, and It ran with dynamic statistics on two columns composite statistics, and not impact the OWNER column since it had the statistics,execution plan base on histogram, and parallel degree downgrade to 1 since No. of records in histogram show zero.
SQL> select /*+ parallel(32) */ avg(last_ddl_time-sysdate), avg(created-sysdate) from test2 where owner = 'USER1' and substr(object_type,1,3)='TAB';
AVG(LAST_DDL_TIME-SYSDATE) AVG(CREATED-SYSDATE)
-------------------------- --------------------
-436.46116 -461.3455
Elapsed: 00:00:00.42
Execution Plan
----------------------------------------------------------
Plan hash value: 2571823673
------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | TQ |IN-OUT| PQ Distrib |
------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 35 | 118 (6)| 00:00:01 | | | |
| 1 | SORT AGGREGATE | | 1 | 35 | | | | | |
| 2 | PX COORDINATOR | | | | | | | | |
| 3 | PX SEND QC (RANDOM) | :TQ10000 | 1 | 35 | | | Q1,00 | P->S | QC (RAND) |
| 4 | SORT AGGREGATE | | 1 | 35 | | | Q1,00 | PCWP | |
| 5 | PX BLOCK ITERATOR | | 2423K| 80M| 118 (6)| 00:00:01 | Q1,00 | PCWC | |
|* 6 | TABLE ACCESS STORAGE FULL| TEST2 | 2423K| 80M| 118 (6)| 00:00:01 | Q1,00 | PCWP | |
------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
6 - storage("OWNER"='USER1' AND SUBSTR("OBJECT_TYPE",1,3)='TAB')
filter("OWNER"='USER1' AND SUBSTR("OBJECT_TYPE",1,3)='TAB')
Note
-----
- dynamic sampling used for this statement (level=5)
- Degree of Parallelism is 32 because of hint
Statistics
----------------------------------------------------------
100 recursive calls
0 db block gets
200629 consistent gets
0 physical reads
2952 redo size
475 bytes sent via SQL*Net to client
360 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
For column owner=’USER’, the statistics and actual distribute value is matched.
Case 2: We drop the histogram, and keep the column OWNER statistics.
SQL> select /*+ parallel(32) */ avg(last_ddl_time-to_date('01-OCT-2010')), avg(created-to_date('01-OCT-2010')), count(*) from test2 where owner = 'USER2' and substr(object_type,1,1)='T
AVG(LAST_DDL_TIME-TO_DATE('01-OCT-2010')) AVG(CREATED-TO_DATE('01-OCT-2010')) COUNT(*)
----------------------------------------- ----------------------------------- ----------
1291.74533 1000.87406 1065024
Elapsed: 00:00:01.04
Execution Plan
----------------------------------------------------------
Plan hash value: 2571823673
------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | TQ |IN-OUT| PQ Distrib |
------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 38 | 612 (3)| 00:00:01 | | | |
| 1 | SORT AGGREGATE | | 1 | 38 | | | | | |
| 2 | PX COORDINATOR | | | | | | | | |
| 3 | PX SEND QC (RANDOM) | :TQ10000 | 1 | 38 | | | Q1,00 | P->S | QC (RAND) |
| 4 | SORT AGGREGATE | | 1 | 38 | | | Q1,00 | PCWP | |
| 5 | PX BLOCK ITERATOR | | 640K| 23M| 612 (3)| 00:00:01 | Q1,00 | PCWC | |
|* 6 | TABLE ACCESS STORAGE FULL| TEST2 | 640K| 23M| 612 (3)| 00:00:01 | Q1,00 | PCWP | |
------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
6 - storage("OWNER"='USER2' AND SUBSTR("OBJECT_TYPE",1,1)='T')
filter("OWNER"='USER2' AND SUBSTR("OBJECT_TYPE",1,1)='T')
Note
-----
- dynamic sampling used for this statement (level=5)
- Degree of Parallelism is 32 because of hint
Statistics
----------------------------------------------------------
135 recursive calls
0 db block gets
955823 consistent gets
922152 physical reads
4552 redo size
569 bytes sent via SQL*Net to client
360 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
6 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> alter system flush buffer_cache;
System altered.
Elapsed: 00:00:00.06
SQL> alter system flush shared_pool;
System altered.
Elapsed: 00:00:00.01
SQL> select /*+ parallel(32) */ avg(last_ddl_time-to_date('01-OCT-2010')), avg(created-to_date('01-OCT-2010')), count(*) from test2 where owner = 'USER3' and substr(object_type,1,1)='T
AVG(LAST_DDL_TIME-TO_DATE('01-OCT-2010')) AVG(CREATED-TO_DATE('01-OCT-2010')) COUNT(*)
----------------------------------------- ----------------------------------- ----------
1511.14031 1375.30824 3402
Elapsed: 00:00:01.01
Execution Plan
----------------------------------------------------------
Plan hash value: 2571823673
------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | TQ |IN-OUT| PQ Distrib |
------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 38 | 612 (3)| 00:00:01 | | | |
| 1 | SORT AGGREGATE | | 1 | 38 | | | | | |
| 2 | PX COORDINATOR | | | | | | | | |
| 3 | PX SEND QC (RANDOM) | :TQ10000 | 1 | 38 | | | Q1,00 | P->S | QC (RAND) |
| 4 | SORT AGGREGATE | | 1 | 38 | | | Q1,00 | PCWP | |
| 5 | PX BLOCK ITERATOR | | 18067 | 670K| 612 (3)| 00:00:01 | Q1,00 | PCWC | |
|* 6 | TABLE ACCESS STORAGE FULL| TEST2 | 18067 | 670K| 612 (3)| 00:00:01 | Q1,00 | PCWP | |
------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
6 - storage("OWNER"='USER3' AND SUBSTR("OBJECT_TYPE",1,1)='T')
filter("OWNER"='USER3' AND SUBSTR("OBJECT_TYPE",1,1)='T')
Note
-----
- Degree of Parallelism is 32 because of hint
Statistics
----------------------------------------------------------
135 recursive calls
0 db block gets
955823 consistent gets
922152 physical reads
4552 redo size
567 bytes sent via SQL*Net to client
360 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
6 sorts (memory)
0 sorts (disk)
1 rows processed
Delete the histogram, ORACLE use AVG to decide whether full table or indexes, and dynamic statistics only gather on the composite columns which no statistics on them, However, in 12C, ORACLE would automatically add the composite columns into table extended statistics.
If we delete the whole statistics, then dynamic statistics would on OWNER column and composite columns both.
Case 3: No statistics on the table.
SQL> select /*+ parallel(32) */ avg(last_ddl_time-to_date('01-OCT-2010')), avg(created-to_date('01-OCT-2010')), count(*) from test2 where owner = 'USER2' and substr(object_type,1,1)='T';
AVG(LAST_DDL_TIME-TO_DATE('01-OCT-2010')) AVG(CREATED-TO_DATE('01-OCT-2010')) COUNT(*)
----------------------------------------- ----------------------------------- ----------
1291.74533 1000.87406 1065024
Elapsed: 00:00:01.10
Execution Plan
----------------------------------------------------------
Plan hash value: 2571823673
------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | TQ |IN-OUT| PQ Distrib |
------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 46 | 611 (3)| 00:00:01 | | | |
| 1 | SORT AGGREGATE | | 1 | 46 | | | | | |
| 2 | PX COORDINATOR | | | | | | | | |
| 3 | PX SEND QC (RANDOM) | :TQ10000 | 1 | 46 | | | Q1,00 | P->S | QC (RAND) |
| 4 | SORT AGGREGATE | | 1 | 46 | | | Q1,00 | PCWP | |
| 5 | PX BLOCK ITERATOR | | 659K| 28M| 611 (3)| 00:00:01 | Q1,00 | PCWC | |
|* 6 | TABLE ACCESS STORAGE FULL| TEST2 | 659K| 28M| 611 (3)| 00:00:01 | Q1,00 | PCWP | |
------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
6 - storage("OWNER"='USER2' AND SUBSTR("OBJECT_TYPE",1,1)='T')
filter("OWNER"='USER2' AND SUBSTR("OBJECT_TYPE",1,1)='T')
Note
-----
- dynamic sampling used for this statement (level=5)
- Degree of Parallelism is 32 because of hint
Statistics
----------------------------------------------------------
129 recursive calls
0 db block gets
956242 consistent gets
922154 physical reads
4552 redo size
569 bytes sent via SQL*Net to client
360 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
6 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> alter system flush buffer_cache;
System altered.
Elapsed: 00:00:00.07
SQL> alter system flush shared_pool;
System altered.
Elapsed: 00:00:00.01
SQL> select /*+ parallel(32) */ avg(last_ddl_time-to_date('01-OCT-2010')), avg(created-to_date('01-OCT-2010')), count(*) from test2 where owner = 'USER3' and substr(object_type,1,1)='T';
AVG(LAST_DDL_TIME-TO_DATE('01-OCT-2010')) AVG(CREATED-TO_DATE('01-OCT-2010')) COUNT(*)
----------------------------------------- ----------------------------------- ----------
1511.14031 1375.30824 3402
Elapsed: 00:00:01.07
Execution Plan
----------------------------------------------------------
Plan hash value: 2571823673
------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | TQ |IN-OUT| PQ Distrib |
------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 46 | 611 (3)| 00:00:01 | | | |
| 1 | SORT AGGREGATE | | 1 | 46 | | | | | |
| 2 | PX COORDINATOR | | | | | | | | |
| 3 | PX SEND QC (RANDOM) | :TQ10000 | 1 | 46 | | | Q1,00 | P->S | QC (RAND) |
| 4 | SORT AGGREGATE | | 1 | 46 | | | Q1,00 | PCWP | |
| 5 | PX BLOCK ITERATOR | | 5398 | 242K| 611 (3)| 00:00:01 | Q1,00 | PCWC | |
|* 6 | TABLE ACCESS STORAGE FULL| TEST2 | 5398 | 242K| 611 (3)| 00:00:01 | Q1,00 | PCWP | |
------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
6 - storage("OWNER"='USER3' AND SUBSTR("OBJECT_TYPE",1,1)='T')
filter("OWNER"='USER3' AND SUBSTR("OBJECT_TYPE",1,1)='T')
Note
-----
- dynamic sampling used for this statement (level=5)
- Degree of Parallelism is 32 because of hint
Statistics
----------------------------------------------------------
129 recursive calls
0 db block gets
956242 consistent gets
922154 physical reads
4552 redo size
567 bytes sent via SQL*Net to client
360 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
6 sorts (memory)
0 sorts (disk)
1 rows processed
Optimizer didn’t pick up the index even the fetching % is typical small. The reason is force parallel runs, would disable index acess. If remove the hint and set optimizer on policy = AUTO mode.
Case 5: No statistics and parallel policy = AUTO
SQL> select /*+ */ avg(last_ddl_time-to_date('01-OCT-2010')), avg(created-to_date('01-OCT-2010')), count(*) from test2 where owner = 'USER2' and substr(object_type,1,1)='T';
AVG(LAST_DDL_TIME-TO_DATE('01-OCT-2010')) AVG(CREATED-TO_DATE('01-OCT-2010')) COUNT(*)
----------------------------------------- ----------------------------------- ----------
1291.74533 1000.87406 1065024
Elapsed: 00:00:02.34
Execution Plan
----------------------------------------------------------
Plan hash value: 2571823673
------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | TQ |IN-OUT| PQ Distrib |
------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 46 | 6522 (3)| 00:00:01 | | | |
| 1 | SORT AGGREGATE | | 1 | 46 | | | | | |
| 2 | PX COORDINATOR | | | | | | | | |
| 3 | PX SEND QC (RANDOM) | :TQ10000 | 1 | 46 | | | Q1,00 | P->S | QC (RAND) |
| 4 | SORT AGGREGATE | | 1 | 46 | | | Q1,00 | PCWP | |
| 5 | PX BLOCK ITERATOR | | 659K| 28M| 6522 (3)| 00:00:01 | Q1,00 | PCWC | |
|* 6 | TABLE ACCESS STORAGE FULL| TEST2 | 659K| 28M| 6522 (3)| 00:00:01 | Q1,00 | PCWP | |
------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
6 - storage("OWNER"='USER2' AND SUBSTR("OBJECT_TYPE",1,1)='T')
filter("OWNER"='USER2' AND SUBSTR("OBJECT_TYPE",1,1)='T')
Note
-----
- dynamic sampling used for this statement (level=5)
- automatic DOP: Computed Degree of Parallelism is 3
Statistics
----------------------------------------------------------
45 recursive calls
4 db block gets
926142 consistent gets
922157 physical reads
4656 redo size
569 bytes sent via SQL*Net to client
360 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
6 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> alter system flush buffer_cache;
System altered.
Elapsed: 00:00:00.06
SQL> alter system flush shared_pool;
System altered.
Elapsed: 00:00:00.00
SQL> select /*+ */ avg(last_ddl_time-to_date('01-OCT-2010')), avg(created-to_date('01-OCT-2010')), count(*) from test2 where owner = 'USER3' and substr(object_type,1,1)='T';
AVG(LAST_DDL_TIME-TO_DATE('01-OCT-2010')) AVG(CREATED-TO_DATE('01-OCT-2010')) COUNT(*)
----------------------------------------- ----------------------------------- ----------
1511.14031 1375.30824 3402
Elapsed: 00:00:20.48
Execution Plan
----------------------------------------------------------
Plan hash value: 1204039106
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 46 | 1436 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 46 | | |
|* 2 | TABLE ACCESS BY INDEX ROWID| TEST2 | 5398 | 242K| 1436 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | INDEX1_TEST2 | 10159 | | 45 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(SUBSTR("OBJECT_TYPE",1,1)='T')
3 - access("OWNER"='USER3')
Note
-----
- dynamic sampling used for this statement (level=2)
- automatic DOP: Computed Degree of Parallelism is 1 because of parallel threshold
Statistics
----------------------------------------------------------
32 recursive calls
0 db block gets
28382 consistent gets
28015 physical reads
1458488 redo size
567 bytes sent via SQL*Net to client
360 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
6 sorts (memory)
0 sorts (disk)
1 rows processed
When policy = auto, Optimizer would estimate the running time, if less than 30 seconds (or 10 seconds), it would run parallel. Then Optimizer able to compare the cost by statistics value or dynamic statistics value.
Dynamic statistics be used for optimizer to decide the access path, but statistics in dictionary is not enough; and for optimizer to decide whether should run the parallel or not if the session on auto DOP mode. When join with other table, dynamic statistics would be gathered on the columns cross the tables, and in 12C, it may be recorded and act as extended column – cluster tables.