unstable plans
set lines 200 pages 9999
col execs for 999,999,999
col min_etime for 999,999.99
col max_etime for 999,999.99
col avg_etime for 999,999.999
col avg_lio for 999,999,999.9
col norm_stddev for 999,999.9999
col begin_interval_time for a30
col node for 99999
break on plan_hash_value on startup_time skip 1
select * from (
select sql_id, sum(execs), min(avg_etime) min_etime, max(avg_etime) max_etime, stddev_etime/min(avg_etime) norm_stddev
from (
select sql_id, plan_hash_value, execs, avg_etime,
stddev(avg_etime) over (partition by sql_id) stddev_etime
from (
select sql_id, plan_hash_value,
sum(nvl(executions_delta,0)) execs,
(sum(elapsed_time_delta)/decode(sum(nvl(executions_delta,0)),0,1,sum(executions_delta))/1000000) avg_etime
-- sum((buffer_gets_delta/decode(nvl(buffer_gets_delta,0),0,1,executions_delta))) avg_lio
from DBA_HIST_SQLSTAT S, DBA_HIST_SNAPSHOT SS
where ss.snap_id = S.snap_id
and ss.instance_number = S.instance_number
and executions_delta > 0
group by sql_id, plan_hash_value
)
)
group by sql_id, stddev_etime
)
where norm_stddev > nvl(to_number('&min_stddev'),2)
and max_etime > nvl(to_number('&min_etime'),.1)
order by norm_stddev
/
-- whats_changed_c.sql
DEF days_of_history_accessed = '5';
DEF captured_at_least_x_times = '1';
DEF captured_at_least_x_days_apart = '5';
DEF med_elap_microsecs_threshold = '1e4';
DEF min_slope_threshold = '0.1';
DEF max_num_rows = '20';
SET lin 300 ver OFF;
set linesize 500
COL row_n format A2 HEA '#';
COL change format A13 justify left;
COL slope format 9999.99 HEA 'SLOPE' justify right;
COL pctdbtim format 99.99 HEA 'Total Elapsed|% DB Time' justify right;
COL med_secs_per_exec format a15 HEA 'Median Secs|Per Exec' justify right;
COL std_secs_per_exec format a15 HEA 'Std Dev Secs|Per Exec' justify right;
COL avg_secs_per_exec format a15 HEA 'Avg Secs|Per Exec' justify right;
COL min_secs_per_exec format a15 HEA 'Min Secs|Per Exec' justify right;
COL max_secs_per_exec format a15 HEA 'Max Secs|Per Exec' justify right;
COL plans format 9999 justify right;
COL sql_text_80 format A80 justify left;
PRO SQL Statements with "Elapsed Time per Execution" changing over time
WITH
per_time AS
(
SELECT
h.dbid,
h.sql_id,
SYSDATE - CAST(s.end_interval_time AS DATE) days_ago,
SUM(h.elapsed_time_delta) / SUM(decode(h.executions_delta,0,0.00000001,h.executions_delta)) time_per_exec,
--SUM(h.elapsed_time_delta) time_per_exec,
SUM(decode(h.executions_delta,0,0.00000001,h.executions_delta)) execs
FROM
dba_hist_sqlstat h,
dba_hist_snapshot s
WHERE s.snap_id = h.snap_id
AND s.dbid = h.dbid
AND s.instance_number = h.instance_number
AND h.parsing_schema_name NOT IN ('SYS','SYSTEM','SYSTEM2','DBSNMP')
AND h.executions_delta > 0
AND CAST(s.end_interval_time AS DATE) > SYSDATE - &&days_of_history_accessed.
GROUP BY
h.dbid,
h.sql_id,
SYSDATE - CAST(s.end_interval_time AS DATE)
HAVING
SUM(decode(h.executions_delta,0,0.00000001,h.executions_delta)) >= 1
)
,
db_time AS
(
SELECT
tdbtim
FROM
(
(
SELECT
e.stat_name ,
(e.value - NVL(b.value,0)) value
FROM
dba_hist_sys_time_model b ,
dba_hist_sys_time_model e
WHERE
e.dbid = b.dbid
AND e.instance_number = b.instance_number
AND e.snap_id =
(
SELECT
MAX(snap_id)
FROM
dba_hist_snapshot
WHERE
CAST(end_interval_time AS DATE) > SYSDATE - &&days_of_history_accessed.
)
AND b.snap_id =
(
SELECT
MIN(snap_id)
FROM
dba_hist_snapshot
WHERE
CAST(end_interval_time AS DATE) > SYSDATE - &&days_of_history_accessed.
)
AND b.stat_id = e.stat_id
AND e.stat_name IN ('DB time','DB CPU',
'background elapsed time','background cpu time')
)
pivot (SUM(value) FOR stat_name IN ('DB time' tdbtim)) )
)
,
sql_dbt AS
(
SELECT
h.sql_id,
ROUND(h.time_per_exec*100/d.tdbtim,2) pctdbtim
FROM
(
SELECT
sql_id,
SUM(time_per_exec*execs) time_per_exec
FROM
per_time
GROUP BY
sql_id
)
h,
db_time d
)
,
avg_time AS
(
SELECT
dbid,
sql_id,
MEDIAN(time_per_exec) med_time_per_exec,
STDDEV(time_per_exec) std_time_per_exec,
AVG(time_per_exec) avg_time_per_exec,
MIN(time_per_exec) min_time_per_exec,
MAX(time_per_exec) max_time_per_exec
FROM
per_time
GROUP BY
dbid,
sql_id
HAVING
COUNT(*) >= &&captured_at_least_x_times.
--AND MAX(days_ago) - MIN(days_ago) >=
-- &&captured_at_least_x_days_apart.
AND MEDIAN(time_per_exec) > &&med_elap_microsecs_threshold.
)
,
time_over_median AS
(
SELECT
h.dbid,
h.sql_id,
h.days_ago,
(h.time_per_exec / a.med_time_per_exec) time_per_exec_over_med,
a.med_time_per_exec,
a.std_time_per_exec,
a.avg_time_per_exec,
a.min_time_per_exec,
a.max_time_per_exec
FROM
per_time h,
avg_time a
WHERE
a.sql_id = h.sql_id
)
,
ranked AS
(
SELECT
RANK () OVER (ORDER BY ABS(REGR_SLOPE(t.time_per_exec_over_med,t.days_ago)) DESC) rank_num,
t.dbid,
t.sql_id,
CASE
WHEN REGR_SLOPE(t.time_per_exec_over_med, t.days_ago) > 0
THEN 'IMPROVING'
ELSE 'REGRESSING'
END change,
ROUND(REGR_SLOPE(t.time_per_exec_over_med, t.days_ago), 3)
slope,
ROUND(AVG(t.med_time_per_exec)/1e6, 3) med_secs_per_exec,
ROUND(AVG(t.std_time_per_exec)/1e6, 3) std_secs_per_exec,
ROUND(AVG(t.avg_time_per_exec)/1e6, 3) avg_secs_per_exec,
ROUND(MIN(t.min_time_per_exec)/1e6, 3) min_secs_per_exec,
ROUND(MAX(t.max_time_per_exec)/1e6, 3) max_secs_per_exec
FROM
time_over_median t
GROUP BY
t.dbid,
t.sql_id
HAVING
ABS(REGR_SLOPE(t.time_per_exec_over_med, t.days_ago)) > &&min_slope_threshold.
)
SELECT
row_n,
r.sql_id,
change,
slope,
s.pctdbtim,
lpad(med_secs_per_exec,15,' ') med_secs_per_exec,
lpad(std_secs_per_exec,15,' ') std_secs_per_exec,
lpad(avg_secs_per_exec,15,' ') avg_secs_per_exec,
lpad(min_secs_per_exec,15,' ') min_secs_per_exec,
lpad(max_secs_per_exec,15,' ') max_secs_per_exec,
plans,
sql_text_80
FROM
(
SELECT
LPAD(ROWNUM, 2) row_n,
r.sql_id,
r.change,
--TO_CHAR(r.slope, '990.000MI') slope,
ROUND(r.slope,2) slope,
--TO_CHAR(s.pctdbtim,'99.9999') ela_pct_dbtime,
TO_CHAR(r.med_secs_per_exec, '999,990.000') med_secs_per_exec,
TO_CHAR(r.std_secs_per_exec, '999,990.000') std_secs_per_exec,
TO_CHAR(r.avg_secs_per_exec, '999,990.000') avg_secs_per_exec,
TO_CHAR(r.min_secs_per_exec, '999,990.000') min_secs_per_exec,
TO_CHAR(r.max_secs_per_exec, '999,990.000') max_secs_per_exec,
(
SELECT
COUNT(DISTINCT p.plan_hash_value)
FROM
dba_hist_sql_plan p
WHERE
p.dbid = r.dbid
AND p.sql_id = r.sql_id
)
plans,
REPLACE(
(
SELECT distinct
sys.DBMS_LOB.SUBSTR(s.sql_text, 80)
FROM
dba_hist_sqltext s
WHERE
s.dbid = r.dbid
AND s.sql_id = r.sql_id
)
, CHR(10)) sql_text_80
FROM
ranked r
WHERE
r.rank_num <= &&max_num_rows.
ORDER BY
r.rank_num
)
r,
sql_dbt s
WHERE
r.sql_id = s.sql_id
ORDER BY
row_n
/
Total Elapsed Median Secs Std Dev Secs Avg Secs Min Secs Max Secs
# SQL_ID CHANGE SLOPE % DB Time Per Exec Per Exec Per Exec Per Exec Per Exec PLANS SQL_TEXT_80
-- ------------- ------------- -------- ------------- --------------- --------------- --------------- --------------- --------------- ----- --------------------------------------------------------------------------------
1 00nxwgnnnhd9z REGRESSING -.62 .15 151.788 140.202 222.579 131.888 384.062 2 SELECT AUDITSIGNATURE,SEQUENCEGENERATORPOOLNAME,SEQUENCEGENERATORID,SEQUENCENUMB
2 02jrgb8ppzbpx IMPROVING .13 .32 262.992 168.498 285.982 53.514 523.788 0 call FTRESS_FT.purge_exp_sessions ( )
COL force_matching_signature format 999999999999999999999999 HEA 'FORCE_MATCHING' justify right;
WITH
per_time AS
(
SELECT
h.dbid,
--h.sql_id,
h.force_matching_signature,
SYSDATE - CAST(s.end_interval_time AS DATE) days_ago,
SUM(h.elapsed_time_delta) / SUM(decode(h.executions_delta,0,0.00000001,h.executions_delta)) time_per_exec,
--SUM(h.elapsed_time_delta) time_per_exec,
SUM(decode(h.executions_delta,0,0.00000001,h.executions_delta)) execs
FROM
dba_hist_sqlstat h,
dba_hist_snapshot s
WHERE
h.executions_delta > 0
AND s.snap_id = h.snap_id
AND s.dbid = h.dbid
AND s.instance_number = h.instance_number
AND h.parsing_schema_name NOT IN ('SYS','SYSTEM','SYSTEM2', 'DBSNMP')
AND CAST(s.end_interval_time AS DATE) > SYSDATE - &&days_of_history_accessed.
GROUP BY
h.dbid,
--h.sql_id,
h.force_matching_signature,
SYSDATE - CAST(s.end_interval_time AS DATE)
HAVING
SUM(decode(h.executions_delta,0,0.00000001,h.executions_delta)) >= 1
)
,
db_time AS
(
SELECT
tdbtim,
tdbcpu,
tbgtim,
tbgcpu
FROM
(
(
SELECT
e.stat_name ,
(e.value - NVL(b.value,0)) value
FROM
dba_hist_sys_time_model b ,
dba_hist_sys_time_model e
WHERE
e.dbid = b.dbid
AND e.instance_number = b.instance_number
AND e.snap_id =
(
SELECT
MAX(snap_id)
FROM
dba_hist_snapshot
WHERE
CAST(end_interval_time AS DATE) > SYSDATE - &&days_of_history_accessed.
)
AND b.snap_id =
(
SELECT
MIN(snap_id)
FROM
dba_hist_snapshot
WHERE
CAST(end_interval_time AS DATE) > SYSDATE - &&days_of_history_accessed.
)
AND b.stat_id = e.stat_id
AND e.stat_name IN ('DB time','DB CPU' ,'background elapsed time','background cpu time')
)
pivot (SUM(value) FOR stat_name IN ('DB time' tdbtim ,'DB CPU' tdbcpu ,'background elapsed time' tbgtim ,'background cpu time' tbgcpu)))
)
,
sql_dbt AS
(
SELECT
--h.sql_id,
h.force_matching_signature,
ROUND(h.time_per_exec*100/d.tdbtim,2) pctdbtim
FROM
(
SELECT
--sql_id,
force_matching_signature,
SUM(time_per_exec*execs) time_per_exec
FROM
per_time
GROUP BY
--sql_id
force_matching_signature
)
h,
db_time d
)
,
avg_time AS
(
SELECT
dbid,
--sql_id,
force_matching_signature,
MEDIAN(time_per_exec) med_time_per_exec,
STDDEV(time_per_exec) std_time_per_exec,
AVG(time_per_exec) avg_time_per_exec,
MIN(time_per_exec) min_time_per_exec,
MAX(time_per_exec) max_time_per_exec
FROM
per_time
GROUP BY
dbid,
--sql_id
force_matching_signature
HAVING
COUNT(*) >= &&captured_at_least_x_times.
--AND MAX(days_ago) - MIN(days_ago) >=
-- &&captured_at_least_x_days_apart.
AND MEDIAN(time_per_exec) > &&med_elap_microsecs_threshold.
)
,
time_over_median AS
(
SELECT
h.dbid,
--h.sql_id,
h.force_matching_signature,
h.days_ago,
(h.time_per_exec / a.med_time_per_exec) time_per_exec_over_med,
a.med_time_per_exec,
a.std_time_per_exec,
a.avg_time_per_exec,
a.min_time_per_exec,
a.max_time_per_exec
FROM
per_time h,
avg_time a
--WHERE a.sql_id = h.sql_id
WHERE
a.force_matching_signature = h.force_matching_signature
)
,
ranked AS
(
SELECT
RANK () OVER (ORDER BY ABS(REGR_SLOPE(t.time_per_exec_over_med,
t.days_ago)) DESC) rank_num,
t.dbid,
--t.sql_id,
t.force_matching_signature,
CASE
WHEN REGR_SLOPE(t.time_per_exec_over_med, t.days_ago) > 0
THEN 'IMPROVING'
ELSE 'REGRESSING'
END change,
ROUND(REGR_SLOPE(t.time_per_exec_over_med, t.days_ago), 3)
slope,
ROUND(AVG(t.med_time_per_exec)/1e6, 3) med_secs_per_exec,
ROUND(AVG(t.std_time_per_exec)/1e6, 3) std_secs_per_exec,
ROUND(AVG(t.avg_time_per_exec)/1e6, 3) avg_secs_per_exec,
ROUND(MIN(t.min_time_per_exec)/1e6, 3) min_secs_per_exec,
ROUND(MAX(t.max_time_per_exec)/1e6, 3) max_secs_per_exec
FROM
time_over_median t
GROUP BY
t.dbid,
--t.sql_id
t.force_matching_signature
HAVING
ABS(REGR_SLOPE(t.time_per_exec_over_med, t.days_ago)) > &&min_slope_threshold.
)
SELECT
row_n,
--r.sql_id,
r.force_matching_signature,
change,
slope,
s.pctdbtim,
lpad(med_secs_per_exec,15,' ') med_secs_per_exec,
lpad(std_secs_per_exec,15,' ') std_secs_per_exec,
lpad(avg_secs_per_exec,15,' ') avg_secs_per_exec,
lpad(min_secs_per_exec,15,' ') min_secs_per_exec,
lpad(max_secs_per_exec,15,' ') max_secs_per_exec
-- plans,
--sql_text_80
FROM
(
SELECT
LPAD(ROWNUM, 2) row_n,
--r.sql_id,
r.force_matching_signature,
r.change,
--TO_CHAR(r.slope, '990.000MI') slope,
ROUND(r.slope,2) slope,
--TO_CHAR(s.pctdbtim,'99.9999') ela_pct_dbtime,
TO_CHAR(r.med_secs_per_exec, '999,990.000') med_secs_per_exec,
TO_CHAR(r.std_secs_per_exec, '999,990.000') std_secs_per_exec,
TO_CHAR(r.avg_secs_per_exec, '999,990.000') avg_secs_per_exec,
TO_CHAR(r.min_secs_per_exec, '999,990.000') min_secs_per_exec,
TO_CHAR(r.max_secs_per_exec, '999,990.000') max_secs_per_exec
--(SELECT COUNT(DISTINCT p.plan_hash_value) FROM
-- dba_hist_sql_plan p WHERE p.dbid = r.dbid AND p.sql_id =
-- r.sql_id) plans,
--REPLACE((SELECT sys.DBMS_LOB.SUBSTR(s.sql_text, 80) FROM
-- dba_hist_sqltext s WHERE s.dbid = r.dbid AND s.sql_id =
-- r.sql_id), CHR(10)) sql_text_80
FROM
ranked r
WHERE
r.rank_num <= &&max_num_rows.
ORDER BY
r.rank_num
)
r,
sql_dbt s
WHERE
r.force_matching_signature = s.force_matching_signature
ORDER BY
row_n
/
Total Elapsed Median Secs Std Dev Secs Avg Secs Min Secs Max Secs
# FORCE_MATCHING CHANGE SLOPE % DB Time Per Exec Per Exec Per Exec Per Exec Per Exec
-- ------------------------- ------------- -------- ------------- --------------- --------------- --------------- --------------- ---------------
1 15936051349132279638 REGRESSING -.62 .15 151.788 140.202 222.579 131.888 384.062
SQL>
min_elapsed_time
define min_elapsed_time=10
define min_repeat_executions_filter=10
col 1 FOR 99999
col 2 FOR 99999
col 3 FOR 9999
col 4 FOR 999
col 5 FOR 99
col av FOR 99999
col ct FOR 99999
col mn FOR 999
col av FOR 99999.9
col MAX_RUN_TIME FOR a40
col longest_sql_exec_id FOR A20
set linesize 500
set pages 9999
WITH pivot_data AS
(SELECT sql_id,
ct,
mxdelta mx,
mndelta mn,
ROUND(avdelta) av,
WIDTH_BUCKET(delta_in_seconds,mndelta,mxdelta+.1,5) AS bucket ,
SUBSTR(times,12) max_run_time,
SUBSTR(longest_sql_exec_id, 12) longest_sql_exec_id
FROM
(SELECT sql_id,
delta_in_seconds,
COUNT(*) OVER (PARTITION BY sql_id) ct,
MAX(delta_in_seconds) OVER (PARTITION BY sql_id) mxdelta,
MIN(delta_in_seconds) OVER (PARTITION BY sql_id) mndelta,
AVG(delta_in_seconds) OVER (PARTITION BY sql_id) avdelta,
MAX(times) OVER (PARTITION BY sql_id) times,
MAX(longest_sql_exec_id) OVER (PARTITION BY sql_id) longest_sql_exec_id
FROM
(SELECT sql_id,
sql_exec_id,
MAX(delta_in_seconds) delta_in_seconds ,
LPAD(ROUND(MAX(delta_in_seconds),0),10)
|| ' '
|| TO_CHAR(MIN(start_time),'YY-MM-DD HH24:MI:SS')
|| ' '
|| TO_CHAR(MAX(end_time),'YY-MM-DD HH24:MI:SS') times,
LPAD(ROUND(MAX(delta_in_seconds),0),10)
|| ' '
|| TO_CHAR(MAX(sql_exec_id)) longest_sql_exec_id
FROM
( SELECT sql_id, TO_CHAR(sql_exec_id)||'_'||to_char(sql_exec_start,'J') sql_exec_id,
CAST(sample_time AS DATE) end_time,
CAST(sql_exec_start AS DATE) start_time,
((CAST(sample_time AS DATE)) - (CAST(sql_exec_start AS DATE))) * (3600*24) delta_in_seconds
FROM dba_hist_active_sess_history
WHERE sql_exec_id IS NOT NULL
AND sql_id='00nxwgnnnhd9z'
)
GROUP BY sql_id,
sql_exec_id
)
)
WHERE ct >
&min_repeat_executions_filter
AND mxdelta >
&min_elapsed_time )
SELECT *
FROM pivot_data PIVOT ( COUNT(*) FOR bucket IN (1,2,3,4,5))
ORDER BY mx DESC,
av DESC ;
5
------------- ------ ---------- ---- -------- ---------------------------------------- -------------------- ------ ------ ----- ---- ---
00nxwgnnnhd9z 43 1123 411 995.0 24-07-30 16:32:52 24-07-30 16:51:35 16777217_2460522 2 1 0 3 37
SQL>
from https://blog.go-faster.co.uk/2019/10/purging-sql-statements-and-execution.html
set lines 300 pages 99
col costpctdiff heading 'Cost|%Diff' format 99999
col costdiff heading 'Cost|Diff' format 99999999
col plan_hash_value heading 'SQL Plan|Hash Value'
col child_number heading 'Child|No.' format 9999
col inst_id heading 'Inst|ID' format 999
col hcost heading 'AWR|Cost' format 99999999999
col ccost heading 'Cursor|Cost' format 9999999
col htimestamp heading 'AWR|Timestamp'
col ctimestamp heading 'Cursor|Timestamp'
col end_interval_time format a26
col snap_id heading 'Snap|ID' format 99999999
col awr_cost format 9999999999999
col optimizer_Cost heading 'Opt.|Cost' format 99999999
col optimizer_env_hash_value heading 'Opt. Env.|Hash Value'
col num_stats heading 'Num|Stats' format 9999
alter session set nls_date_format = 'hh24:mi:ss dd.mm.yy';
break on plan_hash_value skip 1 on sql_id on dbid
ttitle 'compare AWR/recent plan costs'
with h as ( /*captured plan outside retention limit*/
select p.dbid, p.sql_id, p.plan_hash_Value, max(cost) cost
, max(p.timestamp) timestamp
from dba_hist_sql_plan p
, dba_hist_wr_control c
where p.dbid = c.dbid
and p.cost>0
and (p.object_owner != 'SYS' OR p.object_owner IS NULL) --omit SYS owned objects
and p.timestamp < sysdate-c.retention
group by p.dbid, p.sql_id, p.plan_hash_value
), s as ( /*SQL statistics*/
select t.dbid, t.sql_id, t.plan_hash_value, t.optimizer_env_hash_value
, t.optimizer_cost
, MIN(t.snap_id) snap_id
, MIN(s.end_interval_time) end_interval_time
, COUNT(*) num_stats
from dba_hist_snapshot s
, dba_hist_sqlstat t
where s.dbid = t.dbid
and s.snap_id = t.snap_id
and t.optimizer_cost > 0
GROUP BY t.dbid, t.sql_id, t.plan_hash_value, t.optimizer_env_hash_value
, t.optimizer_cost
), x as (
Select NVL(h.dbid,s.dbid) dbid
, NVL(h.sql_id,s.sql_id) sql_id
, NVL(h.plan_hash_value,s.plan_hash_value) plan_hash_value
, h.cost hcost, h.timestamp htimestamp
, s.snap_id, s.end_interval_time
, s.optimizer_env_hash_value, s.optimizer_cost
, s.num_stats
, s.optimizer_cost-h.cost costdiff
, 100*s.optimizer_cost/NULLIF(h.cost,0) costpctdiff
From h join s
on h.plan_hash_value = s.plan_hash_value
and h.sql_id = s.sql_id
and h.dbid = s.dbid
), y as (
SELECT x.*
, MAX(ABS(costpctdiff)) OVER (PARTITION BY dbid, sql_id, plan_hash_value) maxcostpctdiff
, MAX(ABS(costdiff)) OVER (PARTITION BY dbid, sql_id, plan_hash_value) maxcostabsdiff
FROM x
)
SELECT dbid, sql_id, plan_hash_value, hcost, htimestamp
, snap_id, end_interval_time, optimizer_env_hash_value, optimizer_cost, num_stats, costdiff, costpctdiff
FROM y
WHERE maxcostpctdiff>=10
And maxcostabsdiff>=10
order by plan_hash_value,sql_id,end_interval_time
/
break on report
ttitle off