Showing posts with label SQL tuning advisor from awr snap. Show all posts
Showing posts with label SQL tuning advisor from awr snap. Show all posts

Saturday, April 5, 2025

How to run SQL tuning advisor when SQL is aged out of cursor cache

 Problem statement:Application team reported one of the application query was taking time and you were reported.You got the sql_id either from awr/ash report which exactly matches with the query provided by application team.Now you analyze execution plan and tends to run SQL tuning advisor to see what improvement options it can offer.You tried to run tuning advisor manually and you get the error message saying The SQL has aged out of the cursor cache

Lets see how we can resolve this issue

Reproduce the case:

lets take a random sql_id and try to find whether it is in cursor cache.Simple query which can be used below

SQL> select sql_id from v$sql where sql_id='ajq0rjdv5p34z';

SQL_ID
-------------
ajq0rjdv5p34z

There are various way to run SQL tuning advisor calling set of PL/SQL code,from OEM and script.
Out of these I like oracle supplied script in case you dont have OEM license and remembering PL/SQL code is not an option for you.

execute script from  $ORACLE_HOME/rdbms/admin/sqltrpt.sql which asks sql_id as input parameter but before that it will show 15 most expensive SQL in cursor cahe.It also lists out our intended SQL highlighted

Don't you think this is amazing feature....

15 Most expensive SQL in the cursor cache
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

SQL_ID           ELAPSED
------------- ----------
SQL_TEXT_FRAGMENT
-------------------------------------------------------
4xm1ruvkx3awx   5,916.14
DECLARE job BINARY_INTEGER := :job;  next_date TIMESTAM
1fb9p2aq7dvbg   3,547.03
INSERT                 INTO                    CI_BILL
ajq0rjdv5p34z   2,810.85
select billcycles0_.BILL_CYC_CD as col_0_0_, billcycles
b6usrg82hwsa3   2,482.74

It immediately list outs recommendation like below
1- SQL Profile Finding (see explain plans section below)
--------------------------------------------------------
  A potentially better execution plan was found for this statement.

  Recommendation (estimated benefit: 79.46%)
  ------------------------------------------
  - Consider accepting the recommended SQL profile.
    execute dbms_sqltune.accept_sql_profile(task_name => 'TASK_37088',
            task_owner => 'SYS', replace => TRUE);

  Validation results
  ------------------
  The SQL profile was tested by executing both its plan and the original plan
  and measuring their respective execution statistics. A plan may have been
  only partially executed if the other could be run to completion in less time
.

                           Original Plan  With SQL Profile  % Improved
                           -------------  ----------------  ----------
  Completion Status:            COMPLETE          COMPLETE
  Elapsed Time (s):             .000912           .000124       86.4 %
  CPU Time (s):                 .000879           .000096      89.07 %
  User I/O Time (s):                  0                 0
  Buffer Gets:                      224                46      79.46 %
  Physical Read Requests:             0                 0
  Physical Write Requests:            0                 0
  Physical Read Bytes:                0                 0
  Physical Write Bytes:               0                 0
  Rows Processed:                     1                 1
  Fetches:                            1                 1
  Executions:                         1                 1

  Notes
  -----
  1. Statistics for the original plan were averaged over 10 executions.
  2. Statistics for the SQL profile plan were averaged over 10 executions.

Now I will flush shared_pool so that sql gets aged out of cursor cache.

SQL> select ADDRESS, HASH_VALUE from V$SQLAREA where SQL_Id='ajq0rjdv5p34z';

ADDRESS          HASH_VALUE
---------------- ----------
0000000181BF7E68 1985645727

1 row selected.

SQL> exec DBMS_SHARED_POOL.PURGE('0000000181BF7E68,1985645727','C');

PL/SQL procedure successfully completed.

SQL> select ADDRESS, HASH_VALUE from V$SQLAREA where SQL_Id='ajq0rjdv5p34z';

no rows selected

Now I am executing again

Specify the Sql id
~~~~~~~~~~~~~~~~~~
Enter value for sqlid: ajq0rjdv5p34z

Sql Id specified: ajq0rjdv5p34z

Tune the sql
~~~~~~~~~~~~
ERROR: statement is not in the cursor cache or the workload repository.
Execute the statement and try again

Get the awr snap_id like below
SQL> select snap_id from dba_hist_sqlstat where sql_id='ajq0rjdv5p34z';

   SNAP_ID
----------
       671
       672

Then, you can create a tuning task using the DBMS_SQLTUNE.CREATE_TUNING_TASK procedure, specifying the begin_snap and end_snap values, as well as the SQL ID of the statement to be tuned.

set serveroutput on
declare
l_sql_tune_task_id varchar2(100);
begin
l_sql_tune_task_id := dbms_sqltune.create_tuning_task (
begin_snap => 671,
end_snap => 672,
sql_id => 'ajq0rjdv5p34z',
scope => dbms_sqltune.scope_comprehensive,
time_limit => 10800,
task_name => 'SQLTUNE',
description => 'task description');
dbms_output.put_line('l_sql_tune_task_id: ' || l_sql_tune_task_id);
end;
/

BEGIN
  DBMS_SQLTUNE.execute_tuning_task(task_name => 'SQLTUNE');
END;
/

SET LONG 10000;
SET PAGESIZE 1000
SET LINESIZE 200
SELECT DBMS_SQLTUNE.report_tuning_task('SQLTUNE') AS recommendations FROM dual;

Hurray! now you got the same result
  Recommendation (estimated benefit: 79.47%)
  ------------------------------------------
  - Consider accepting the recommended SQL profile.
    execute dbms_sqltune.accept_sql_profile(task_name => 'SQLTUNE',
            task_owner => 'SYS', replace => TRUE);

  Validation results
  ------------------
  The SQL profile was tested by executing both its plan and the original plan
  and measuring their respective execution statistics. A plan may have been
  only partially executed if the other could be run to completion in less time.

                           Original Plan  With SQL Profile  % Improved
                           -------------  ----------------  ----------
  Completion Status:            COMPLETE          COMPLETE
  Elapsed Time (s):             .001606           .000196      87.79 %
  CPU Time (s):                 .001658           .000152      90.83 %
  User I/O Time (s):                  0                 0
  Buffer Gets:                      224                46      79.46 %
  Physical Read Requests:             0                 0
  Physical Write Requests:            0                 0
  Physical Read Bytes:                0                 0
  Physical Write Bytes:               0                 0
  Rows Processed:                     1                 1
  Fetches:                            1                 1
  Executions:                         1                 1

So save this set of statement as a .sql file like bwlow and try if your sql is aged out of cursor cache..


set serveroutput on
select snap_id from dba_hist_sqlstat where sql_id='&&sql_id';

declare
l_sql_tune_task_id varchar2(100);
begin
l_sql_tune_task_id := dbms_sqltune.create_tuning_task (
begin_snap => &begin_snap,
end_snap => &end_snap,
sql_id => '&&sql_id',
scope => dbms_sqltune.scope_comprehensive,
time_limit => 10800,
task_name => '&&Tuning_task_name',
description => 'task description');
dbms_output.put_line('l_sql_tune_task_id: ' || l_sql_tune_task_id);
end;
/

BEGIN
  DBMS_SQLTUNE.execute_tuning_task(task_name => '&&Tuning_task_name');
END;
/

SET LONG 10000;
SET PAGESIZE 1000
SET LINESIZE 200
SELECT DBMS_SQLTUNE.report_tuning_task('&&Tuning_task_name') AS recommendations FROM dual;

I hope you have learnt something new...

Happy learning...
Please ask question/comment if you still have any question






When SQL plan baseline is not used

 Thank you very much for your interest. Problem Statement: Lets assume you are reported about an application query running slow in one envir...