Monday, March 26, 2012

SQL Plan Management: SQL Plan Baseline Example

At my site, we are upgrading Oracle Application from 11.5.9 to Rel12.1.3 and many of Oracle's program are performing very poorly. We logged service requests with Oracle support but it was little time consuming to get the fix, so we decided to experiment with sql plan baselines for sql statements with bad plan.

This is what i did to see how sql plan baselines work.

Create table SKM_SQL_PLAN_MGMT_TEST as select * from dba_objects so that we have a table with good number of rows.

Execute “select * from SKM_SQL_PLAN_MGMT_TEST where objecT_name = 'AP_INVOICES_ALL';”. At this moment there is no index on SKM_SQL_PLAN_MGMT_TEST table, so execution plan will use full table scan on the table SKM_SQL_PLAN_MGMT_TEST. This is the plan used by sql



SQL> select * from table(dbms_xplan.display_cursor());

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
SQL_ID  4v8quwv000yrr, child number 0
-------------------------------------
select * from SKM_SQL_PLAN_MGMT_TEST where objecT_name =
'AP_INVOICES_ALL'

Plan hash value: 325872721

--------------------------------------------------------------------------------------------
| Id  | Operation         | Name                   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |                        |       |       |  1677 (100)|          |
|*  1 |  TABLE ACCESS FULL| SKM_SQL_PLAN_MGMT_TEST |    68 | 14076 |  1677   (1)| 00:00:21 |
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("OBJECT_NAME"='AP_INVOICES_ALL')

Note
-----
   - dynamic sampling used for this statement (level=2)


23 rows selected.
Run this query to get sql_id for the above sql statement

select sql_id, a.* from gv$sql a where sql_text = 'select * from SKM_SQL_PLAN_MGMT_TEST where objecT_name = ''AP_INVOICES_ALL'''; 

Use the sql_id returned by the above sql and run following script. (Sql_id in this case was 4v8quwv000yrr)

declare
   l_ret_value NUMBER;
begin
   l_ret_value :=DBMS_SPM.load_plans_from_cursor_cache(sql_id=> ‘4v8quwv000yrr’);
end;
/

It will create a base line plan that is enabled and accepted. Detail of this baseline can be seen using this query

SELECT * FROM dba_sql_plan_baselines;

Now create an index on object_name column of SKM_SQL_PLAN_MGMT_TEST

create index SKM_SQL_PLAN_MGMT_TEST_N1 on  db_gx3mn0.SKM_SQL_PLAN_MGMT_TEST (object_name)
/

Now run “select * from SKM_SQL_PLAN_MGMT_TEST where objecT_name = 'AP_INVOICES_ALL';”.

Now check the execution plan used by the above query
set long 20000
set pages 100
set lines 132
select * from table(dbms_xplan.display_cursor());

You will see following output :

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  4v8quwv000yrr, child number 1
-------------------------------------
select * from SKM_SQL_PLAN_MGMT_TEST where objecT_name = 'AP_INVOICES_ALL'

Plan hash value: 325872721

--------------------------------------------------------------------------------------------
| Id  | Operation         | Name                   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |                        |       |       |  1677 (100)|          |
|*  1 |  TABLE ACCESS FULL| SKM_SQL_PLAN_MGMT_TEST |  5017 |  1014K|  1677   (1)| 00:00:21 |
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("OBJECT_NAME"='AP_INVOICES_ALL')

Note
-----
- SQL plan baseline SQL_PLAN_068dj65793y565b6edfd3 used for this statement


23 rows selected.


Although index access provides better execution plan but because of baseline defined for sql statement, query uses bad plan. This demonstrates that once you define baseline for sql statement, that baseline plan will get executed even if Oracle optimizer find a supposedly better plan.

Now if you run the query “select * from dba_sql_plan_baselines where to_char(sql_text) = 'select * from SKM_SQL_PLAN_MGMT_TEST where objecT_name = ''AP_INVOICES_ALL'''”, you will find that one more sql plan baseline is added and it has low cost but value of ACCEPTED column is “NO”. so plan is available for review and could marked as accept for future use. To make plan acceptable, do the following

Run sql “select sql_handle, plan_name, a.* from dba_sql_plan_baselines a where to_char(sql_text) = 'select * from SKM_SQL_PLAN_MGMT_TEST where objecT_name = ''AP_INVOICES_ALL''';”

Now drop the sql plan baseline that does full table scan using following script

declare
  l_ret_value NUMBER;
begin
  l_ret_value := DBMS_SPM.DROP_SQL_PLAN_BASELINE
                 ( SQL_HANDLE      => 'SQL_0321b1314e91f8a6'
                 , PLAN_NAME       => 'SQL_PLAN_068dj65793y565b6edfd3'
                 );
end;
/

Now make the better plan that was generated automatically “ACCEPTED” using following script

declare

  l_clob CLOB;
begin
  l_clob      := DBMS_SPM.evolve_sql_plan_baseline
                 ( sql_handle   => 'SQL_0321b1314e91f8a6'
                 , plan_name    => 'SQL_PLAN_068dj65793y56ae570ac6'
                 , time_limit   => 10
                 , verify       => 'YES'
                 , commit       => 'YES'
                 ) ;
end;
/

Now run the query “select * from SKM_SQL_PLAN_MGMT_TEST where objecT_name = 'AP_INVOICES_ALL';”

Now run “select * from table(dbms_xplan.display_cursor());”, you will see following output

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
SQL_ID  4v8quwv000yrr, child number 0
-------------------------------------
select * from SKM_SQL_PLAN_MGMT_TEST where objecT_name = 'AP_INVOICES_ALL'

Plan hash value: 3071631708

----------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name                      | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                           |       |       |     5 (100)|          |
|   1 |  TABLE ACCESS BY INDEX ROWID| SKM_SQL_PLAN_MGMT_TEST    |     3 |   621 |     5   (0)| 00:00:01
|*  2 |   INDEX RANGE SCAN          | SKM_SQL_PLAN_MGMT_TEST_N1 |     3 |       |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("OBJECT_NAME"='AP_INVOICES_ALL')

Note
-----
   - dynamic sampling used for this statement (level=2)
- SQL plan baseline SQL_PLAN_068dj65793y56ae570ac6 used for this statement


25 rows selected.

Please notice that new sql baseline plan is used for this execution.

No comments:

Post a Comment