oracle-databaseoracle11gsql-execution-planoracle-enterprise-manager

How to force accept a SQL Plan Baseline?


In Oracle (looking at this in Oracle Enterprise Manager) I have a particular SQL Plan Baseline which is set to Enabled = YES.

However, I can't get the ACCEPTED = YES to work. According to the documentation, you need ENABLED and ACCEPTED both.

I tried to "evolve" the Baseline but OEM complains (via a Report) that it's 1.06 times worse. That's not true though.

Also I would like to know how to ensure it does not auto-purge over time and that's it's fixed. Thanks!


Solution

  • To enable baseline usage, optimizer_use_sql_plan_baselines must be true.

    SELECT * FROM dba_sql_plan_baselines
    

    Consider a baseline for the plan with the lowest cost or best elapsed time. The optimizer will choose the lowest cost accepted plan but will give preference to a fixed plan. This is the best way to guarantee the CBO to use a plan, despite what hints and SQL profiles there are for a plan.

    Use your baseline is loaded then get the sql handle from the dba_sql_plan_baselines view.

    Try to evolve it using:

    SET LONG 10000
    SELECT DBMS_SPM.evolve_sql_plan_baseline(sql_handle => 'SQL_handle_xxxxxx') FROM dual;
    
    var report clob;
    exec :report := dbms_spm.evolve_sql_plan_baseline();
    print :report
    

    Evolution of the plan will only work as more plans become available.

    Sometimes you need to fix it to force the baseline, for example:

    SET SERVEROUTPUT ON
    DECLARE
      l_plans_altered  PLS_INTEGER;
    BEGIN
      l_plans_altered := DBMS_SPM.alter_sql_plan_baseline(
        sql_handle      => 'SQL_handle_xxxxxx',
        plan_name       => 'SQL_PLAN_xxxxxxx',
        attribute_name  => 'fixed',
        attribute_value => 'YES');
      DBMS_OUTPUT.put_line('Plans Altered: ' || l_plans_altered);
    END;
    /
    

    Usually the plan will not be used immediately and there are various methods to try and force it...

    One is to kill all sessions running that statement, if viable.

    You can also invalidate the cursor using the table:

    begin
      dbms_stats.gather_table_stats(ownname=> '<schema>',
      tabname=> '<table>', no_invalidate => FALSE);
    end;
    

    Analyzing using the old method will also invalidate the cursor!

    SQL> analyze table <table> estimate statistics sample 1 percent;
    

    To check:

    SQL> select child_number, executions, parse_calls, loads, invalidations 
         from v$sql where sql_id = '<SQLID>';
    

    Sequence of events:

    1. Lock the user
    2. Invalidate the cursor
    3. Kill all sessions logged in as that user
    4. Unlock the user
    5. Check again