The new Oracle 12c optimizer adaptive features are just great and work well out of the box in most cases.
Recently, however, I’ve experienced my very first problem with SQL Plan Directives migrating a database to 12c, so I would like to share it.
Disclaimer 1: this is a specific problem that I found on ONE system. My solution may not fit with your environment, don’t use it if you are not sure about what you’re doing!
Disclaimer 2: despite I had this problem with a single SPD, I like adaptive features and I encourage to use them!!
Problem: a query takes a sub-second in 11gR2, in 12c it takes 12 seconds or more.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
--11gR2 SQL> select * from APPUSER.V_TAB_PROP where TAB_ID = 842300; ... 48 rows selected. Elapsed: 00:00:00.71 --12c SQL> select * from APPUSER.V_TAB_PROP where TAB_ID = 842300; ... 48 rows selected. Elapsed: 00:00:12.74 |
V_TAB_PROP is a very simple view. It just selects a central table “TAB” and then takes different properties by joining a property table “TAB_PROP”.
To do that, it does 11 joins on the same property table.
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 |
create view ... as select ... from TAB li left join (select v.TAB_PROP_ID, v.PROP_VAL as c89 from TAB_PROP v where v.PROP_ID = 89) v89 on li.TAB_PROP_ID = v89.TAB_PROP_ID left join (select v.TAB_PROP_ID, v.PROP_VAL as c88 from TAB_PROP v where v.PROP_ID = 88) v88 on li.TAB_PROP_ID = v88.TAB_PROP_ID left join (select v.TAB_PROP_ID, v.PROP_VAL as c90 from TAB_PROP v where v.PROP_ID = 90) v90 on li.TAB_PROP_ID = v90.TAB_PROP_ID left join (select v.TAB_PROP_ID, v.PROP_VAL as c82 from TAB_PROP v where v.PROP_ID = 82) v82 on li.TAB_PROP_ID = v82.TAB_PROP_ID left join (select v.TAB_PROP_ID, v.PROP_VAL as c84 from TAB_PROP v where v.PROP_ID = 84) v84 on li.TAB_PROP_ID = v84.TAB_PROP_ID left join (select v.TAB_PROP_ID, v.PROP_VAL as c93 from TAB_PROP v where v.PROP_ID = 93) v93 on li.TAB_PROP_ID = v93.TAB_PROP_ID left join (select v.TAB_PROP_ID, v.PROP_VAL as c79 from TAB_PROP v where v.PROP_ID = 79) v79 on li.TAB_PROP_ID = v79.TAB_PROP_ID left join (select v.TAB_PROP_ID, v.PROP_VAL as c81 from TAB_PROP v where v.PROP_ID = 81) v81 on li.TAB_PROP_ID = v81.TAB_PROP_ID left join (select v.TAB_PROP_ID, v.PROP_VAL as c96 from TAB_PROP v where v.PROP_ID = 96) v96 on li.TAB_PROP_ID = v96.TAB_PROP_ID left join (select v.TAB_PROP_ID, v.PROP_VAL as c95 from TAB_PROP v where v.PROP_ID = 95) v95 on li.TAB_PROP_ID = v95.TAB_PROP_ID left join (select v.TAB_PROP_ID, v.PROP_VAL as c94 from TAB_PROP v where v.PROP_ID = 94) v94 on li.TAB_PROP_ID = v94.TAB_PROP_ID ); |
On the property table, TAB_PROP_ID and PROP_ID are unique (they compose the pk), so nested loops and index unique scans are the best way to get this data.
The table is 1500Mb big and the index 1000Mb.
This was the plan in 11g:
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 |
---------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 3401 (100)| | | 1 | NESTED LOOPS OUTER | | 1009 | 218K| 3401 (1)| 00:00:41 | | 2 | NESTED LOOPS OUTER | | 615 | 123K| 2171 (1)| 00:00:27 | | 3 | NESTED LOOPS OUTER | | 390 | 73320 | 1391 (1)| 00:00:17 | | 4 | NESTED LOOPS OUTER | | 248 | 42408 | 894 (0)| 00:00:11 | | 5 | NESTED LOOPS OUTER | | 160 | 24640 | 574 (0)| 00:00:07 | | 6 | NESTED LOOPS OUTER | | 104 | 14248 | 366 (0)| 00:00:05 | | 7 | NESTED LOOPS OUTER | | 68 | 8160 | 230 (0)| 00:00:03 | | 8 | NESTED LOOPS OUTER | | 44 | 4532 | 142 (0)| 00:00:02 | | 9 | NESTED LOOPS OUTER | | 29 | 2494 | 84 (0)| 00:00:02 | | 10 | NESTED LOOPS OUTER | | 19 | 1311 | 46 (0)| 00:00:01 | | 11 | NESTED LOOPS OUTER | | 13 | 676 | 20 (0)| 00:00:01 | | 12 | TABLE ACCESS BY INDEX ROWID| TAB | 8 | 280 | 4 (0)| 00:00:01 | |* 13 | INDEX RANGE SCAN | FK_TAB_PROP | 8 | | 3 (0)| 00:00:01 | | 14 | TABLE ACCESS BY INDEX ROWID| TAB_PROP | 1 | 17 | 2 (0)| 00:00:01 | |* 15 | INDEX UNIQUE SCAN | PK_TAB_PROP | 1 | | 1 (0)| 00:00:01 | | 16 | TABLE ACCESS BY INDEX ROWID | TAB_PROP | 2 | 34 | 2 (0)| 00:00:01 | |* 17 | INDEX UNIQUE SCAN | PK_TAB_PROP | 1 | | 1 (0)| 00:00:01 | | 18 | TABLE ACCESS BY INDEX ROWID | TAB_PROP | 2 | 34 | 2 (0)| 00:00:01 | |* 19 | INDEX UNIQUE SCAN | PK_TAB_PROP | 1 | | 1 (0)| 00:00:01 | | 20 | TABLE ACCESS BY INDEX ROWID | TAB_PROP | 2 | 34 | 2 (0)| 00:00:01 | |* 21 | INDEX UNIQUE SCAN | PK_TAB_PROP | 1 | | 1 (0)| 00:00:01 | | 22 | TABLE ACCESS BY INDEX ROWID | TAB_PROP | 2 | 34 | 2 (0)| 00:00:01 | |* 23 | INDEX UNIQUE SCAN | PK_TAB_PROP | 1 | | 1 (0)| 00:00:01 | | 24 | TABLE ACCESS BY INDEX ROWID | TAB_PROP | 2 | 34 | 2 (0)| 00:00:01 | |* 25 | INDEX UNIQUE SCAN | PK_TAB_PROP | 1 | | 1 (0)| 00:00:01 | | 26 | TABLE ACCESS BY INDEX ROWID | TAB_PROP | 2 | 34 | 2 (0)| 00:00:01 | |* 27 | INDEX UNIQUE SCAN | PK_TAB_PROP | 1 | | 1 (0)| 00:00:01 | | 28 | TABLE ACCESS BY INDEX ROWID | TAB_PROP | 2 | 34 | 2 (0)| 00:00:01 | |* 29 | INDEX UNIQUE SCAN | PK_TAB_PROP | 1 | | 1 (0)| 00:00:01 | | 30 | TABLE ACCESS BY INDEX ROWID | TAB_PROP | 2 | 34 | 2 (0)| 00:00:01 | |* 31 | INDEX UNIQUE SCAN | PK_TAB_PROP | 1 | | 1 (0)| 00:00:01 | | 32 | TABLE ACCESS BY INDEX ROWID | TAB_PROP | 2 | 34 | 2 (0)| 00:00:01 | |* 33 | INDEX UNIQUE SCAN | PK_TAB_PROP | 1 | | 1 (0)| 00:00:01 | | 34 | TABLE ACCESS BY INDEX ROWID | TAB_PROP | 2 | 34 | 2 (0)| 00:00:01 | |* 35 | INDEX UNIQUE SCAN | PK_TAB_PROP | 1 | | 1 (0)| 00:00:01 | ---------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 13 - access("LI"."TAB_ID"=842300) 15 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID" AND "V"."PROP_ID"=94) 17 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID" AND "V"."PROP_ID"=93) 19 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID" AND "V"."PROP_ID"=79) 21 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID" AND "V"."PROP_ID"=96) 23 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID" AND "V"."PROP_ID"=84) 25 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID" AND "V"."PROP_ID"=95) 27 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID" AND "V"."PROP_ID"=82) 29 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID" AND "V"."PROP_ID"=81) 31 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID" AND "V"."PROP_ID"=88) 33 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID" AND "V"."PROP_ID"=89) 35 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID" AND "V"."PROP_ID"=90) |
In 12c, the plan switches to adaptive, and half of the joins are converted to hash joins / full table scans:
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 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
--------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | --------------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 48 | | * 1 | HASH JOIN RIGHT OUTER | | 1 | 829K| 48 | | * 2 | TABLE ACCESS FULL | TAB_PROP | 1 | 2486K| 2486K| | * 3 | HASH JOIN OUTER | | 1 | 539K| 48 | |- 4 | NESTED LOOPS OUTER | | 1 | 539K| 48 | |- 5 | STATISTICS COLLECTOR | | 1 | | 48 | | * 6 | HASH JOIN OUTER | | 1 | 350K| 48 | |- 7 | NESTED LOOPS OUTER | | 1 | 350K| 48 | |- 8 | STATISTICS COLLECTOR | | 1 | | 48 | | * 9 | HASH JOIN OUTER | | 1 | 228K| 48 | |- 10 | NESTED LOOPS OUTER | | 1 | 228K| 48 | |- 11 | STATISTICS COLLECTOR | | 1 | | 48 | | * 12 | HASH JOIN OUTER | | 1 | 148K| 48 | |- 13 | NESTED LOOPS OUTER | | 1 | 148K| 48 | |- 14 | STATISTICS COLLECTOR | | 1 | | 48 | | * 15 | HASH JOIN OUTER | | 1 | 96510 | 48 | |- 16 | NESTED LOOPS OUTER | | 1 | 96510 | 48 | |- 17 | STATISTICS COLLECTOR | | 1 | | 48 | | * 18 | HASH JOIN OUTER | | 1 | 62771 | 48 | |- 19 | NESTED LOOPS OUTER | | 1 | 62771 | 48 | |- 20 | STATISTICS COLLECTOR | | 1 | | 48 | |- * 21 | HASH JOIN OUTER | | 1 | 40827 | 48 | | 22 | NESTED LOOPS OUTER | | 1 | 40827 | 48 | |- 23 | STATISTICS COLLECTOR | | 1 | | 48 | |- * 24 | HASH JOIN OUTER | | 1 | 26554 | 48 | | 25 | NESTED LOOPS OUTER | | 1 | 26554 | 48 | |- 26 | STATISTICS COLLECTOR | | 1 | | 48 | |- * 27 | HASH JOIN OUTER | | 1 | 17271 | 48 | | 28 | NESTED LOOPS OUTER | | 1 | 17271 | 48 | |- 29 | STATISTICS COLLECTOR | | 1 | | 48 | |- * 30 | HASH JOIN OUTER | | 1 | 11305 | 48 | | 31 | NESTED LOOPS OUTER | | 1 | 11305 | 48 | |- 32 | STATISTICS COLLECTOR | | 1 | | 48 | | 33 | BATCHED TABLE ACCESS BY INDEX ROWID | TAB | 1 | 9 | 48 | | * 34 | INDEX RANGE SCAN | FK_TAB_PROP | 1 | 9 | 48 | | 35 | TABLE ACCESS BY INDEX ROWID | TAB_PROP | 48 | 1326 | 48 | | * 36 | INDEX UNIQUE SCAN | PK_TAB_PROP | 48 | 1 | 48 | |- * 37 | TABLE ACCESS FULL | TAB_PROP | 0 | 1326 | 0 | | 38 | TABLE ACCESS BY INDEX ROWID | TAB_PROP | 48 | 2 | 48 | | * 39 | INDEX UNIQUE SCAN | PK_TAB_PROP | 48 | 1 | 48 | |- * 40 | TABLE ACCESS FULL | TAB_PROP | 0 | 2 | 0 | | 41 | TABLE ACCESS BY INDEX ROWID | TAB_PROP | 48 | 2 | 48 | | * 42 | INDEX UNIQUE SCAN | PK_TAB_PROP | 48 | 1 | 48 | |- * 43 | TABLE ACCESS FULL | TAB_PROP | 0 | 2 | 0 | | 44 | TABLE ACCESS BY INDEX ROWID | TAB_PROP | 48 | 2 | 48 | | * 45 | INDEX UNIQUE SCAN | PK_TAB_PROP | 48 | 1 | 48 | |- * 46 | TABLE ACCESS FULL | TAB_PROP | 0 | 2 | 0 | |- 47 | TABLE ACCESS BY INDEX ROWID | TAB_PROP | 0 | 2 | 0 | |- * 48 | INDEX UNIQUE SCAN | PK_TAB_PROP | 0 | | 0 | | * 49 | TABLE ACCESS FULL | TAB_PROP | 1 | 2486K| 2486K| |- 50 | TABLE ACCESS BY INDEX ROWID | TAB_PROP | 0 | 2 | 0 | |- * 51 | INDEX UNIQUE SCAN | PK_TAB_PROP | 0 | | 0 | | * 52 | TABLE ACCESS FULL | TAB_PROP | 1 | 2486K| 2486K| |- 53 | TABLE ACCESS BY INDEX ROWID | TAB_PROP | 0 | 2 | 0 | |- * 54 | INDEX UNIQUE SCAN | PK_TAB_PROP | 0 | | 0 | | * 55 | TABLE ACCESS FULL | TAB_PROP | 1 | 2486K| 2486K| |- 56 | TABLE ACCESS BY INDEX ROWID | TAB_PROP | 0 | 2 | 0 | |- * 57 | INDEX UNIQUE SCAN | PK_TAB_PROP | 0 | | 0 | | * 58 | TABLE ACCESS FULL | TAB_PROP | 1 | 2486K| 2486K| |- 59 | TABLE ACCESS BY INDEX ROWID | TAB_PROP | 0 | 2 | 0 | |- * 60 | INDEX UNIQUE SCAN | PK_TAB_PROP | 0 | | 0 | | * 61 | TABLE ACCESS FULL | TAB_PROP | 1 | 2486K| 2486K| |- 62 | TABLE ACCESS BY INDEX ROWID | TAB_PROP | 0 | 2 | 0 | |- * 63 | INDEX UNIQUE SCAN | PK_TAB_PROP | 0 | | 0 | | * 64 | TABLE ACCESS FULL | TAB_PROP | 1 | 2486K| 2486K| --------------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID") 2 - filter("V"."PROP_ID"=84) 3 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID") 6 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID") 9 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID") 12 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID") 15 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID") 18 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID") 21 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID") 24 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID") 27 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID") 30 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID") 34 - access("LI"."TAB_ID"=842300) 36 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID" AND "V"."PROP_ID"=94) 37 - filter("V"."PROP_ID"=94) 39 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID" AND "V"."PROP_ID"=89) 40 - filter("V"."PROP_ID"=89) 42 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID" AND "V"."PROP_ID"=93) 43 - filter("V"."PROP_ID"=93) 45 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID" AND "V"."PROP_ID"=90) 46 - filter("V"."PROP_ID"=90) 48 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID" AND "V"."PROP_ID"=79) 49 - filter("V"."PROP_ID"=79) 51 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID" AND "V"."PROP_ID"=81) 52 - filter("V"."PROP_ID"=81) 54 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID" AND "V"."PROP_ID"=96) 55 - filter("V"."PROP_ID"=96) 57 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID" AND "V"."PROP_ID"=95) 58 - filter("V"."PROP_ID"=95) 60 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID" AND "V"."PROP_ID"=88) 61 - filter("V"."PROP_ID"=88) 63 - access("LI"."TAB_PROP_ID"="V"."TAB_PROP_ID" AND "V"."PROP_ID"=82) 64 - filter("V"."PROP_ID"=82) Note ----- - dynamic statistics used: dynamic sampling (level=2) - this is an adaptive plan (rows marked '-' are inactive) - 1 Sql Plan Directive used for this statement |
However, the inflection point is never reached. The execution keeps the default plan that has half of the joins HJ and the other half NL.
The problem in this case is the SQL Directive. Why?
There are to many distinct values for TAB_ID and the data is very skewed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
-- without adaptive features SQL> alter session set optimizer_adaptive_features=false ; Session altered. SQL> select * from APPUSER.V_TAB_PROP where TAB_ID = 842300; ... 48 rows selected. Elapsed: 00:00:00.23 -- with adaptive features SQL> alter session set optimizer_adaptive_features=true; Session altered. SQL> select * from APPUSER.V_TAB_PROP where TAB_ID = 842300; ... 48 rows selected. Elapsed: 00:00:13.84 |
The histogram on that column is OK and it always leads to the correct plan (with the adaptive features disabled).
But there are still some “minor” misestimates, and the optimizer sometimes decides to create a SQL Plan directive:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
SQL> select DIRECTIVE_ID, TYPE, ENABLED, REASON, NOTES from dba_sql_plan_directives where directive_id in (select directive_id from dba_sql_plan_dir_objects where object_name='TAB_PROP'); DIRECTIVE_ID TYPE ENA REASON NOTES -------------------- ---------------- --- ------------------------------------ -------------------------------------------------------------------------------- 5347794880142580861 DYNAMIC_SAMPLING YES JOIN CARDINALITY MISESTIMATE <spd_note><internal_state>PERMANENT</internal_state><redundant>NO</redundant><spd_text>{F(APPUSER.TAB) - F(APPUSER.TAB_PROP)}</spd_text></spd_note> 5473412518742433352 DYNAMIC_SAMPLING YES JOIN CARDINALITY MISESTIMATE <spd_note><internal_state>HAS_STATS</internal_state><redundant>NO</redundant><spd_text>{(APPUSER.TAB) - F(APPUSER.TAB) - F(APPUSER.TAB_PROP)}</spd_text></spd_note> 14420228120434685523 DYNAMIC_SAMPLING YES JOIN CARDINALITY MISESTIMATE <spd_note><internal_state>HAS_STATS</internal_state><redundant>NO</redundant><spd_text>{F(APPUSER.CHAMP) - (APPUSER.TAB) - F(APPUSER.TAB) - F(APPUSER.TAB_PROP)}</spd_text></spd_note> |
The Directive instructs the optimizer to do a dynamic sampling, but with a such big and skewed table this is not ok, so the Dynamic sampling result is worse than using the histogram. I can check it by simplifying the query to just one join:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
-- with dynamic sampling/sql plan directive: ------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | ------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 48 | | 1 | NESTED LOOPS OUTER | | 1 | 11305 | 48 | | 2 | TABLE ACCESS BY INDEX ROWID BATCHED| TAB | 1 | 9 | 48 | |* 3 | INDEX RANGE SCAN | FK_TAB_PROP | 1 | 9 | 48 | | 4 | TABLE ACCESS BY INDEX ROWID | TAB_PROP | 48 | 1326 | 48 | |* 5 | INDEX UNIQUE SCAN | PK_TAB_PROP | 48 | 1 | 48 | ------------------------------------------------------------------------------------------- -- without dynamic sampling ------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | ------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 48 | | 1 | NESTED LOOPS OUTER | | 1 | 13 | 48 | | 2 | TABLE ACCESS BY INDEX ROWID BATCHED| TAB | 1 | 9 | 48 | |* 3 | INDEX RANGE SCAN | FK_TAB_PROP | 1 | 9 | 48 | | 4 | TABLE ACCESS BY INDEX ROWID | TAB_PROP | 48 | 2 | 48 | |* 5 | INDEX UNIQUE SCAN | PK_TAB_PROP | 48 | 1 | 48 | ------------------------------------------------------------------------------------------- |
What’s the fix?
I’ve tried to drop the directive first, but it reappears as soon as there are new misestimates.
The best solution in my case has been to disable the directive, an operation that can be done easily with the DBMS_SPD package:
1 2 3 4 5 6 7 8 |
BEGIN FOR rec in (select d.directive_id as did from dba_sql_plan_directives d join dba_sql_plan_dir_objects o on (d.directive_id=o.directive_id) where o.owner='APPUSER' and o.object_name in ('TAB','TAB_PROP')) LOOP DBMS_SPD.ALTER_SQL_PLAN_DIRECTIVE ( rec.did, 'ENABLED','NO'); END LOOP; END; / |
I did this on a QAS environment.
Because the production system is not migrated to 12c yet, it’s wise to import these disabled directives in production before the optimizer creates and enables them.
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 |
-- export from the source SET SERVEROUTPUT ON DECLARE my_list DBMS_SPD.OBJECTTAB := DBMS_SPD.ObjectTab(); dir_cnt NUMBER; BEGIN DBMS_SPD.CREATE_STGTAB_DIRECTIVE (table_name => 'TAB_PROP_DIRECTIVES', table_owner=> 'SYSTEM' ); my_list.extend(2); -- TAB table my_list(1).owner := 'APPUSER'; my_list(1).object_name := 'TAB'; my_list(1).object_type := 'TABLE'; -- TAB_PROP table my_list(2).owner := 'APPUSER'; my_list(2).object_name := 'TAB_PROP'; my_list(2).object_type := 'TABLE'; dir_cnt := dir_cnt := DBMS_SPD.PACK_STGTAB_DIRECTIVE(table_name => 'TAB_PROP_DIRECTIVES', table_owner=> 'SYSTEM', obj_list => my_list); DBMS_OUTPUT.PUT_LINE('dir_cnt = ' || dir_cnt); END; / expdp directory=data_pump_dir dumpfile=TAB_PROP_DIRECTIVES.dmp logfile=expdp_VAL_LIG_DIRECTIVES.log tables=system.TAB_PROP_DIRECTIVES -- import into the destination impdp directory=data_pump_dir dumpfile=TAB_PROP_DIRECTIVES.dmp logfile=impdp_VAL_LIG_DIRECTIVES.log SELECT DBMS_SPD.UNPACK_STGTAB_DIRECTIVE(table_name => 'TAB_PROP_DIRECTIVES', table_owner=> 'SYSTEM') FROM DUAL; |
Off course, the directives can’t be created for objects that do not exist, the import has to be done after the objects migrate to the 12c version.
Because the SQL Plan Directives are tied to specific objects and not specific queries, they can fix many statements at once, but in case like this one, they can compromise several statements!
Monitoring the creation of new directives is an important task as it may indicate misestimates/lack of statistics on one side or execution plan changes on the other one.
Latest posts by Ludovico (see all)
- New views in Oracle Data Guard 23c - January 3, 2024
- New in Data Guard 21c and 23c: Automatic preparation of the primary - December 22, 2023
- Does FLASHBACK QUERY work across incarnations or after a Data Guard failover? - December 13, 2023
>>SQL Plan Directives are tied to specific objects
<<
Aren't they tied to query expressions?
Hi Jc, no, they are tied to objects or object columns but not query expressions
nice explanation with example
Pingback: 12c: директивы плана выполнения — SQL Plan Directives | Oracle mechanics
Pingback: SQL Plan Directives: they’re always good… except when they’re bad! - Ludovico Caldara - Blogs - triBLOG