How can you tell if a SQL statement will see stale optimizer statistics?

February 15, 2023 | 2 minute read
Nigel Bayliss
Product Manager
Text Size 100%:

Whenever I am sent a SQL statement that's not performing very well, my first question is always, "are stats fresh?". If statistics are stale, the optimizer will still use them because it's the best information it can get (well, in theory, and assuming you're not using dynamic sampling). If the statistics don't match the reality, the optimizer may choose a sub-optimal plan.

You can check every table for stale or missing statistics like this:

Copied to Clipboard
Error: Could not Copy
Copied to Clipboard
Error: Could not Copy
select table_name,owner 
from  dba_tab_statistics 
where stale_stats = 'YES' or stale_stats is null;

However, wouldn't it be nice if you could check your 'problem' query?

If the query has been executed and it's still in the cursor cache (and you have the SQL ID), you can do something like this. Note that I don't claim this is a perfect solution (see the caveat below), but it's pretty cool IMO:

Copied to Clipboard
Error: Could not Copy
Copied to Clipboard
Error: Could not Copy
set pagesize 100
set linesize 150
set trims off
set tab off
set verify off
column table_name format a50
column index_name format a50
column object_type format a40
column owner format a40

accept sql_id prompt 'Enter the SQL ID: ' 
PROMPT ==========
PROMPT Tables
PROMPT ==========
with plan_tables as (
select distinct object_name,object_owner, object_type 
from v$sql_plan 
where object_type like 'TABLE%' 
and   sql_id      = '&sql_id')
select t.object_owner owner,
       t.object_name table_name,
       t.object_type object_type,
       decode(stale_stats,'NO','OK',NULL, 'NO STATS!', 'STALE!') staleness   
from   dba_tab_statistics s,
       plan_tables        t
where  s.table_name = t.object_name
and    s.owner      = t.object_owner
and    s.partition_name is null
and    s.subpartition_name is null
order by t.object_owner, t.object_name;

PROMPT ==========
PROMPT Indexes
PROMPT ==========
with plan_indexes as (
select distinct object_name,object_owner, object_type
from v$sql_plan
where object_type like 'INDEX%'
and   sql_id      = '&sql_id')
select i.object_owner owner,
       i.object_name index_name,
       i.object_type object_type,
       decode(stale_stats,'NO','OK',NULL, 'NO STATS!', 'STALE!') staleness
from   dba_ind_statistics s,
       plan_indexes       i
where  s.index_name = i.object_name
and    s.owner      = i.object_owner
and    s.partition_name is null
and    s.subpartition_name is null
order by i.object_owner, i.object_name;

This will list the staleness status of tables and indexes accessed by the relevant SQL statement. You can also retrieve plans from AWR and SQL tuning sets if the cursor cache doesn't contain the statement you want. Alternatively, you can join the plan data with DBA_TAB_MODIFICATIONS, and see exactly how much DML activity has been going on with the tables accessed by the SQL statement.

I have a caveat! Use this information as a guide. A SQL statement in the cursor cache may have been parsed hours or even days ago, so it could have been parsed under completely different circumstances (maybe all stats were fresh or even wholly stale). In addition, if you have gathered statistics recently (with default parameters), the SQL statement in the cursor cache might not have been re-parsed to see the updated statistics. The re-parse does not happen immediately, so the SQL statement could be using a plan based on stale statistics even if everything looks fresh. Eventually, cursor invalidation will initiate a re-parse and the SQL statement will see the new statistics. Be aware of these possibilities, but at least you can tell whether or not a freshly-parsed SQL statement will see up-to-date statistics.

Nigel Bayliss

Product Manager

Nigel is the product manager for the Oracle Optimizer. He's worked with Oracle technology since 1988 and joined Oracle in 1996. He's been involved in a wide variety of roles including development, benchmarking, consulting and pre-sales. 


Previous Post

Gathering System, Fixed Object and Dictionary Statistics

Nigel Bayliss | 2 min read

Next Post


New SQL Analysis Report in Oracle Database 23c Free!

Nigel Bayliss | 6 min read
Oracle Chatbot
Disconnected