Often, we face an issue where developers shout on us that their query was performing good and now it is behaving bad. There can be many cause for this lag but in this we will see how we can force a query to follow the good execution plan using sql_profile.
get the sql_id first for the problematic statement
Now use the below query to know the details for the sql_id
select PARSING_SCHEMA_NAME, inst_id, sql_id, child_number, plan_hash_value plan_hash, executions execs,
(elapsed_time/1000000)/decode(nvl(executions,0),0,1,executions) avg_etime_secs,
buffer_gets/decode(nvl(executions,0),0,1,executions) avg_lio,
last_active_time,
SQL_PROFILE,
is_shareable, is_bind_sensitive, is_bind_aware,
sql_text,
decode(IO_CELL_OFFLOAD_ELIGIBLE_BYTES,0,'No','Yes') Offload,
decode(IO_CELL_OFFLOAD_ELIGIBLE_BYTES,0,0,100*(IO_CELL_OFFLOAD_ELIGIBLE_BYTES-IO_INTERCONNECT_BYTES)
/decode(IO_CELL_OFFLOAD_ELIGIBLE_BYTES,0,1,IO_CELL_OFFLOAD_ELIGIBLE_BYTES)) "IO_SAVED_%"
from gv$sql s
where sql_id like nvl(trim('&sql_id'),sql_id)
order by 1, 2, 3
Now lets assume that this is the bad plan. So if want to know the good plan for this sql_id, we have a great chance that Oracle has stored the execution history of our sql id in AWR and expose it via the DBA_HIST_SQLSTAT view. o the only thing we have to do is to query this view. Note that AWR stores hourly snapshots and therefore in order to get the rough time that the execution took place you must join to DBA_HIST_SNAPSHOT.
It is important to understand that when you review the history of executions of a specific sql id and you see the list of various execution plans that were used, you must be sure which one of these is the "good" one. The simplest way to verify this is from your ETL execution history (e.g. from your ETL tool metadata), or directly from the user experience saying when did this report run fast etc. Once you know which plan is the good one, and you see that currently you are using a different plan, plus you are experiencing a performance issue, then you know that the change of plan is the one to blame.
Use the below query to know the history of specific sql_id in AWR
select a.INSTANCE_NUMBER, snap_id, BEGIN_INTERVAL_TIME, END_INTERVAL_TIME,
PARSING_SCHEMA_NAME,
sql_id, PLAN_HASH_VALUE,
aa.name command_type_desc,
SQL_PROFILE,
executions_total,
OPTIMIZER_COST,
(ELAPSED_TIME_TOTAL/1e6)/decode(nvl(EXECUTIONS_TOTAL,0),0,1,EXECUTIONS_TOTAL)/
decode(PX_SERVERS_EXECS_TOTAL,0,1,PX_SERVERS_EXECS_TOTAL)/decode(nvl(EXECUTIONS_TOTAL,0),0,1,EXECUTIONS_TOTAL) avg_etime,
decode(PX_SERVERS_EXECS_TOTAL,0,1,PX_SERVERS_EXECS_TOTAL)/decode(nvl(EXECUTIONS_TOTAL,0),0,1,EXECUTIONS_TOTAL) avg_px,
BUFFER_GETS_TOTAL/decode(nvl(EXECUTIONS_TOTAL,0),0,1,EXECUTIONS_TOTAL) avg_lio,
VERSION_COUNT nochild_cursors,
decode(IO_OFFLOAD_ELIG_BYTES_TOTAL,0,'No','Yes') Offload,
decode(IO_OFFLOAD_ELIG_BYTES_TOTAL,0,0,100*(IO_OFFLOAD_ELIG_BYTES_TOTAL-IO_INTERCONNECT_BYTES_TOTAL))
/decode(IO_OFFLOAD_ELIG_BYTES_TOTAL,0,1,IO_OFFLOAD_ELIG_BYTES_TOTAL) "IO_SAVED_%",
c.sql_text
from DBA_HIST_SQLSTAT a left outer join
DBA_HIST_SNAPSHOT b using (SNAP_ID) left outer join
DBA_HIST_SQLTEXT c using (SQL_ID) left outer join
audit_actions aa on (COMMAND_TYPE = aa.ACTION)
where sql_id = nvl(trim('&sql_id'),sql_id)
and b.begin_interval_time > sysdate - &days_back
order by 2 desc,3 desc;
Next section is, how to force a specific execution plan using sql_profiles
1. First find the problematic sql_id and find a way to get it from the views V$SQLSTATS or awr view DBA_HIST_SQLSTAT
2. search for the execution plans for the specific sql id
****************************
prompt
prompt ********************************************************
prompt Find the available execution plans for a specific SQL_ID
prompt ********************************************************
prompt
set linesize 999
col avg_et_secs justify right format 9999999.99
col cost justify right format 9999999999
col timestamp justify center format a25
col parsing_schema_name justify center format a30
col inst_id format 999999999
col executions_total format 99999999999999999
col executions format 99999999999999
alter session set nls_date_format='dd-mm-yyyy hh24:mi:ss';
select 'gv$sqlarea_plan_hash' source, INST_ID,
SQL_ID, PLAN_HASH_VALUE,
executions,
round(elapsed_time/decode(nvl(executions,0),0,1,executions)/1e6/
decode(px_servers_executions,0,1,px_servers_executions)/decode(nvl(executions,0),0,1,executions),2) avg_et_secs,
px_servers_executions/decode(nvl(executions,0),0,1,executions) avg_px,
optimizer_cost cost,
LAST_LOAD_TIME timestamp,
parsing_schema_name --FIRST_LOAD_TIME, LAST_LOAD_TIME, LAST_ACTIVE_TIME, SQL_PROFILE
from gv$sqlarea_plan_hash
where sql_id = nvl(trim('&&sql_id'),sql_id)
UNION
SELECT 'dba_hist_sql_plan' source, null INST_ID,
t1.sql_id sql_id, t1.plan_hash_value plan_hash_value,
t2.executions_total,
t2.avg_et_secs avg_et_secs,
t2.avg_px,
t1.cost cost,
t1.timestamp timestamp,
NULL parsing_schema_name
FROM dba_hist_sql_plan t1,
(
SELECT sql_id, plan_hash_value,
max(executions_total) executions_total, --round(SUM(elapsed_time_total)/decode(SUM(executions_total),0,1,SUM(executions_total))/1e6,2) avg_et_secs
round(SUM(elapsed_time_total)/decode(SUM(executions_total),0,1,SUM(executions_total))/1e6/
decode(SUM(px_servers_execs_total),0,1,SUM(px_servers_execs_total))/decode(SUM(executions_total),0,1,SUM(executions_total)),2) avg_et_secs,
SUM(px_servers_execs_total)/decode(SUM(executions_total),0,1,SUM(executions_total)) avg_px
FROM dba_hist_sqlstat
WHERE
executions_total > 0
GROUP BY sql_id, plan_hash_value
) t2
WHERE
t1.sql_id = nvl(TRIM('&sql_id.'), t1.sql_id)
AND t1.depth = 0
AND t1.sql_id = t2.sql_id(+)
AND t1.plan_hash_value = t2.plan_hash_value(+)
order by avg_et_secs, cost --timestamp desc
/
undef sql_id
*****************************************************************************
based upon the average elapsed type column(AVG_ET_SECS), we can atleast pin point which can be the good plan.
Next using the metalink note:using the script coe_xfr_sql_profile.sql provided with the tool Sqltxplain (Refer to SQLT (SQLTXPLAIN) - Tool that helps to diagnose a SQL statement performing poorly (Metalink Note: 215187.1 and 1487302.1)).
The script will ask for the sql_id and plan_hash value
Now once it is executed, check the explain plan using the sql id and verify if it is using the correct hash value or not.
*********************************
set linesize 9999
set pagesize 999
--set serveroutput off
select * from table( dbms_xplan.display_cursor('&sql_id', '&child_number', 'ALL ALLSTATS LAST -PROJECTION'));
--'ALLSTATS LAST alias partition cost'));
--'ALLSTATS LAST alias'));
--'ALL ALLSTATS LAST'));
--'TYPICAL LAST'));
--'ADVANCED +PEEKED_BINDS +ALLSTATS LAST +MEMSTATS LAST') );
**************************************************************************************************
Please note this is just temporary fix and the actual root has to be investigated in details.////
get the sql_id first for the problematic statement
Now use the below query to know the details for the sql_id
select PARSING_SCHEMA_NAME, inst_id, sql_id, child_number, plan_hash_value plan_hash, executions execs,
(elapsed_time/1000000)/decode(nvl(executions,0),0,1,executions) avg_etime_secs,
buffer_gets/decode(nvl(executions,0),0,1,executions) avg_lio,
last_active_time,
SQL_PROFILE,
is_shareable, is_bind_sensitive, is_bind_aware,
sql_text,
decode(IO_CELL_OFFLOAD_ELIGIBLE_BYTES,0,'No','Yes') Offload,
decode(IO_CELL_OFFLOAD_ELIGIBLE_BYTES,0,0,100*(IO_CELL_OFFLOAD_ELIGIBLE_BYTES-IO_INTERCONNECT_BYTES)
/decode(IO_CELL_OFFLOAD_ELIGIBLE_BYTES,0,1,IO_CELL_OFFLOAD_ELIGIBLE_BYTES)) "IO_SAVED_%"
from gv$sql s
where sql_id like nvl(trim('&sql_id'),sql_id)
order by 1, 2, 3
Now lets assume that this is the bad plan. So if want to know the good plan for this sql_id, we have a great chance that Oracle has stored the execution history of our sql id in AWR and expose it via the DBA_HIST_SQLSTAT view. o the only thing we have to do is to query this view. Note that AWR stores hourly snapshots and therefore in order to get the rough time that the execution took place you must join to DBA_HIST_SNAPSHOT.
It is important to understand that when you review the history of executions of a specific sql id and you see the list of various execution plans that were used, you must be sure which one of these is the "good" one. The simplest way to verify this is from your ETL execution history (e.g. from your ETL tool metadata), or directly from the user experience saying when did this report run fast etc. Once you know which plan is the good one, and you see that currently you are using a different plan, plus you are experiencing a performance issue, then you know that the change of plan is the one to blame.
Use the below query to know the history of specific sql_id in AWR
select a.INSTANCE_NUMBER, snap_id, BEGIN_INTERVAL_TIME, END_INTERVAL_TIME,
PARSING_SCHEMA_NAME,
sql_id, PLAN_HASH_VALUE,
aa.name command_type_desc,
SQL_PROFILE,
executions_total,
OPTIMIZER_COST,
(ELAPSED_TIME_TOTAL/1e6)/decode(nvl(EXECUTIONS_TOTAL,0),0,1,EXECUTIONS_TOTAL)/
decode(PX_SERVERS_EXECS_TOTAL,0,1,PX_SERVERS_EXECS_TOTAL)/decode(nvl(EXECUTIONS_TOTAL,0),0,1,EXECUTIONS_TOTAL) avg_etime,
decode(PX_SERVERS_EXECS_TOTAL,0,1,PX_SERVERS_EXECS_TOTAL)/decode(nvl(EXECUTIONS_TOTAL,0),0,1,EXECUTIONS_TOTAL) avg_px,
BUFFER_GETS_TOTAL/decode(nvl(EXECUTIONS_TOTAL,0),0,1,EXECUTIONS_TOTAL) avg_lio,
VERSION_COUNT nochild_cursors,
decode(IO_OFFLOAD_ELIG_BYTES_TOTAL,0,'No','Yes') Offload,
decode(IO_OFFLOAD_ELIG_BYTES_TOTAL,0,0,100*(IO_OFFLOAD_ELIG_BYTES_TOTAL-IO_INTERCONNECT_BYTES_TOTAL))
/decode(IO_OFFLOAD_ELIG_BYTES_TOTAL,0,1,IO_OFFLOAD_ELIG_BYTES_TOTAL) "IO_SAVED_%",
c.sql_text
from DBA_HIST_SQLSTAT a left outer join
DBA_HIST_SNAPSHOT b using (SNAP_ID) left outer join
DBA_HIST_SQLTEXT c using (SQL_ID) left outer join
audit_actions aa on (COMMAND_TYPE = aa.ACTION)
where sql_id = nvl(trim('&sql_id'),sql_id)
and b.begin_interval_time > sysdate - &days_back
order by 2 desc,3 desc;
Next section is, how to force a specific execution plan using sql_profiles
1. First find the problematic sql_id and find a way to get it from the views V$SQLSTATS or awr view DBA_HIST_SQLSTAT
2. search for the execution plans for the specific sql id
****************************
prompt
prompt ********************************************************
prompt Find the available execution plans for a specific SQL_ID
prompt ********************************************************
prompt
set linesize 999
col avg_et_secs justify right format 9999999.99
col cost justify right format 9999999999
col timestamp justify center format a25
col parsing_schema_name justify center format a30
col inst_id format 999999999
col executions_total format 99999999999999999
col executions format 99999999999999
alter session set nls_date_format='dd-mm-yyyy hh24:mi:ss';
select 'gv$sqlarea_plan_hash' source, INST_ID,
SQL_ID, PLAN_HASH_VALUE,
executions,
round(elapsed_time/decode(nvl(executions,0),0,1,executions)/1e6/
decode(px_servers_executions,0,1,px_servers_executions)/decode(nvl(executions,0),0,1,executions),2) avg_et_secs,
px_servers_executions/decode(nvl(executions,0),0,1,executions) avg_px,
optimizer_cost cost,
LAST_LOAD_TIME timestamp,
parsing_schema_name --FIRST_LOAD_TIME, LAST_LOAD_TIME, LAST_ACTIVE_TIME, SQL_PROFILE
from gv$sqlarea_plan_hash
where sql_id = nvl(trim('&&sql_id'),sql_id)
UNION
SELECT 'dba_hist_sql_plan' source, null INST_ID,
t1.sql_id sql_id, t1.plan_hash_value plan_hash_value,
t2.executions_total,
t2.avg_et_secs avg_et_secs,
t2.avg_px,
t1.cost cost,
t1.timestamp timestamp,
NULL parsing_schema_name
FROM dba_hist_sql_plan t1,
(
SELECT sql_id, plan_hash_value,
max(executions_total) executions_total, --round(SUM(elapsed_time_total)/decode(SUM(executions_total),0,1,SUM(executions_total))/1e6,2) avg_et_secs
round(SUM(elapsed_time_total)/decode(SUM(executions_total),0,1,SUM(executions_total))/1e6/
decode(SUM(px_servers_execs_total),0,1,SUM(px_servers_execs_total))/decode(SUM(executions_total),0,1,SUM(executions_total)),2) avg_et_secs,
SUM(px_servers_execs_total)/decode(SUM(executions_total),0,1,SUM(executions_total)) avg_px
FROM dba_hist_sqlstat
WHERE
executions_total > 0
GROUP BY sql_id, plan_hash_value
) t2
WHERE
t1.sql_id = nvl(TRIM('&sql_id.'), t1.sql_id)
AND t1.depth = 0
AND t1.sql_id = t2.sql_id(+)
AND t1.plan_hash_value = t2.plan_hash_value(+)
order by avg_et_secs, cost --timestamp desc
/
undef sql_id
*****************************************************************************
based upon the average elapsed type column(AVG_ET_SECS), we can atleast pin point which can be the good plan.
Next using the metalink note:using the script coe_xfr_sql_profile.sql provided with the tool Sqltxplain (Refer to SQLT (SQLTXPLAIN) - Tool that helps to diagnose a SQL statement performing poorly (Metalink Note: 215187.1 and 1487302.1)).
The script will ask for the sql_id and plan_hash value
Now once it is executed, check the explain plan using the sql id and verify if it is using the correct hash value or not.
*********************************
set linesize 9999
set pagesize 999
--set serveroutput off
select * from table( dbms_xplan.display_cursor('&sql_id', '&child_number', 'ALL ALLSTATS LAST -PROJECTION'));
--'ALLSTATS LAST alias partition cost'));
--'ALLSTATS LAST alias'));
--'ALL ALLSTATS LAST'));
--'TYPICAL LAST'));
--'ADVANCED +PEEKED_BINDS +ALLSTATS LAST +MEMSTATS LAST') );
**************************************************************************************************
Please note this is just temporary fix and the actual root has to be investigated in details.////