Blessed are the poor is spirit, for theirs is the Kingdom of Heaven.
Mathew 5: 3
We first check if there are any SQL baselines already loaded for the query in PDB2 and drop it if it exists.
SELECT SQL_HANDLE,SQL_TEXT, PLAN_NAME, ENABLED, ACCEPTED FROM sys.DBA_SQL_PLAN_BASELINES where SQL_TEXT LIKE '%select * from cal_cntr where cal_date%';
SQL_HANDLE | SQL_TEXT | PLAN_NAME | ENABLED | ACCEPTED |
---|---|---|---|---|
SQL_937d5a47027e4114 | select * from cal_cntr where cal_date > sysdate - 365 | SQL_PLAN_96zau8w17wh8nb8af81ce | YES | YES |
SQL_937d5a47027e4114 | select * from cal_cntr where cal_date > sysdate - 365 | SQL_PLAN_96zau8w17wh8n2c3ffc18 | NO | NO |
For the given SQL we can see that there are 2 plans already loaded in the database and 1 of them is enabled.
We drop both the existing plans as follows.
DECLARE
ret_value pls_integer;
BEGIN
ret_value := DBMS_SPM.drop_sql_plan_baseline(
sql_handle => 'SQL_937d5a47027e4114',
plan_name => 'SQL_PLAN_96zau8w17wh8n2c3ffc18'
);
END;
/
anonymous block completed
DECLARE
ret_value pls_integer;
BEGIN
ret_value := DBMS_SPM.drop_sql_plan_baseline(
sql_handle => 'SQL_937d5a47027e4114',
plan_name => 'SQL_PLAN_96zau8w17wh8nb8af81ce'
);
END;
/
anonymous block completed
If we now query DBA_SQL_PLAN_BASELINES table it will give "no rows selected".
The below is a scenario where I have two instances (in this case two PDBs) on the same database having a similar configuration and data volumes, (One could be the application database, the other could be a reporting database which is getting replicated using dataguard or any other data replication method).
For some reason the same query (highly unlikely in a PDB scenario on same database), is running well on 1 instance but is using a bad execution plan on the other instance. This is more likely when the setup consists of 2 different databases having similar nature of data and configuration.
The below post is more to illustrate how to transfer a SQL plan from one instance to another.
On PDB1, we baseline the good plan for the query as per the steps in link below.
To Pin the Plan Hash Value for a query using SQL Plan Baseline Below are the steps for the same. We first create the staging table.BEGIN
DBMS_SPM.CREATE_STGTAB_BASELINE(table_name => 'stage');
END;
/
Next we pack the SQL plan baseline using the DBMS_SPM.PACK_STGTAB_BASELINE function.
The SQL_Handle for this basline can be got by querying the DBA_SQL_PLAN_BASELINES table
SELECT SQL_HANDLE,SQL_TEXT, PLAN_NAME, ENABLED FROM sys.DBA_SQL_PLAN_BASELINES where SQL_TEXT LIKE '%select * from cal_cntr where cal_date%';
DECLARE
my_plans number;
BEGIN
my_plans := DBMS_SPM.PACK_STGTAB_BASELINE(
table_name => 'stage',
enabled => 'yes',
SQL_HANDLE => 'SQL_937d5a47027e4114');
END;
/
We use the exp or expdp command to export this STAGE table
expdp system/oracle123@xepdb1 tables=stage directory=TEST_DIR dumpfile=stage.dmp logfile=stage.log
Export: Release 21.0.0.0.0 - Production on Sat Jul 5 10:20:04 2025
Version 21.3.0.0.0
Copyright (c) 1982, 2021, Oracle and/or its affiliates. All rights reserved.
Connected to: Oracle Database 21c Express Edition Release 21.0.0.0.0 - Production
Starting "SYSTEM"."SYS_EXPORT_TABLE_01": system/********@xepdb1 tables=stage directory=TEST_DIR dumpfile=stage.dmp logfile=stage.log;
Processing object type TABLE_EXPORT/TABLE/TABLE_DATA
Processing object type TABLE_EXPORT/TABLE/INDEX/STATISTICS/INDEX_STATISTICS
Processing object type TABLE_EXPORT/TABLE/STATISTICS/TABLE_STATISTICS
Processing object type TABLE_EXPORT/TABLE/STATISTICS/MARKER
Processing object type TABLE_EXPORT/TABLE/TABLE
. . exported "SYSTEM"."STAGE" 41.39 KB 2 rows
Master table "SYSTEM"."SYS_EXPORT_TABLE_01" successfully loaded/unloaded
******************************************************************************
Dump file set for SYSTEM.SYS_EXPORT_TABLE_01 is:
E:\TECHNICAL\BLOGS\POSTS\PERFORMANCE_TUNING\STAGE.DMP
Job "SYSTEM"."SYS_EXPORT_TABLE_01" successfully completed at Sat Jul 5 10:21:10 2021 elapsed 0 00:01:04
These are the tasks on PDB1.
We now connect to PDB2 for importing and unpacking this baseline on PDB2
impdp system/oracle123@xepdb2 tables=stage directory=TEST_DIR dumpfile=stage.dmp logfile=stage_imp.log
Import: Release 21.0.0.0.0 - Production on Sat Jul 5 10:44:24 2025
Version 21.3.0.0.0
Copyright (c) 1982, 2021, Oracle and/or its affiliates. All rights reserved.
Connected to: Oracle Database 21c Express Edition Release 21.0.0.0.0 - Production
Master table "SYSTEM"."SYS_IMPORT_TABLE_01" successfully loaded/unloaded
Starting "SYSTEM"."SYS_IMPORT_TABLE_01": system/********@xepdb2 tables=stage directory=TEST_DIR dumpfile=stage.dmp logfile=stage_imp.log
Processing object type TABLE_EXPORT/TABLE/TABLE
Processing object type TABLE_EXPORT/TABLE/TABLE_DATA
. . imported "SYSTEM"."STAGE" 41.39 KB 2 rows
Processing object type TABLE_EXPORT/TABLE/INDEX/STATISTICS/INDEX_STATISTICS
Processing object type TABLE_EXPORT/TABLE/STATISTICS/TABLE_STATISTICS
Processing object type TABLE_EXPORT/TABLE/STATISTICS/MARKER
Job "SYSTEM"."SYS_IMPORT_TABLE_01" successfully completed at Sat Jul 5 10:45:18 2021 elapsed 0 00:00:42
Once this data is imported, we unpack the SQL plan baseline on PDB2
DECLARE
l_plans_unpacked PLS_INTEGER;
BEGIN
l_plans_unpacked := DBMS_SPM.unpack_stgtab_baseline(
table_name => 'STAGE',
table_owner => 'SYSTEM',
creator => 'SYSTEM');
DBMS_OUTPUT.put_line('Plans Unpacked: ' || l_plans_unpacked);
END;
/
We now query the DBA_SQL_PLAN_BASELINES and verify that the plan has been baselined.
In this scenario, since the container is the same, the sql_id for the query is the same on both the PDBs.
If we now take the plan of the query on PDB2, It shows that it is using the baselined plan.
Plan hash value: 991404550
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 363K| 7462K| 3701 (5)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| CAL_CNTR | 363K| 7462K| 3701 (5)| 00:00:01 |
------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("CAL_DATE">SYSDATE@!-365)
Note
-----
- SQL plan baseline "SQL_PLAN_96zau8w17wh8nb8af81ce" used for this statement
In a scenario where we have two different databases, in that case the sql_id for the queries may be different.
DB1 sql_id1 plan_hash_value1 (good plan)
DB2 sql_id2 plan_hash_value2 (not so good plan. To be replaced.).
In such a case we will baseline the plan for sql_id 2 also, and then replace the plan hash value for this sql_id with the good plan using DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE, or simply disable the bad plan and enable the good plan. DECLARE
cnt number;
BEGIN
cnt := DBMS_SPM.ALTER_SQL_PLAN_BASELINE(sql_handle=>'SQL_937d5a47027e4114', plan_name=>'SQL_PLAN_96zau8w17wh8n2c3ffc18', attribute_name=>'enabled',attribute_value=>'NO');
dbms_output.put_line('cnt ' || cnt);
END;
Now when we run the query and take the explain plan for it it will use the good plan which is baselined in DBA_SQL_PLAN_BASELINES.
No comments:
Post a Comment