Saving Optimiser Stats - 9i

Doug's Oracle Blog

  • Home
  • Papers
  • Books
  • C.V.
  • Fun
  • Oracle Blog
  • Personal Blog

Jun 18: Saving Optimiser Stats - 9i

In a recent blog I described how a mis-timed optimiser statistics collection job led to a bad execution plan for one of the SQL statements in a regular batch job

It's no coincidence that we were already in the process of implementing a change to our stats collection period to retain a short history of stats.


The problem with collecting stats regularly is that the execution plans for SQL statements will probably change at some point. Well, it's not really a problem, is it? That's the whole point of collecting statistics - so that the optimiser can choose an execution plan that makes more sense for your changing data volumes and values. However, what happens when the new execution plan isn't as 'good' as the old one? What happens when (as happened in our case), there was something wrong with the statistics? Well, you'll be in the situation where, not only is a SQL statement running more slowly than expected, but you have no idea why! The solution is to be able to see the 'good' plan before the change and compare it to the 'bad' one. Which is pretty difficult unless you save execution plans across the entire system for later review. However, if we had the previous set of statistics, we could simply reinstate them or restore them into another environment so that we can investigate the plan changes.

Saving optimiser statistics seems like a good idea. That's probably why 10g can do so automatically.

The particular approach we're taking at work is to incorporate a simple bit of code into our stats collection procedure that saves the existing stats first, for a system-specific period of time. For the OLTP systems, that might only be seven days. For a marketing system where some jobs are only run once a month, we might want to keep 5 weeks of statistics.

However, Jonathan Lewis left a comment on the original blog pointing out that there's a simple way of saving one set of statistics if that's sufficient for your needs. It works in all versions of DBMS_STATS and that's what I'm going to show you here - in Oracle 9.2.0.4

First of all the test_schema - note that TEST_TAB doesn't have any statistics on it at the moment.

SQL> select table_name, num_rows from user_tables;


TABLE_NAME NUM_ROWS
------------------------------ ----------
TEST_TAB
TEST_TAB1 65536600
TEST_TAB2 65443100


SQL> select count(*) from test_tab;


COUNT(*)
----------
1000000


So there are really a million rows in that table. If we're going to save stats, we'll need to create a table to save them into. This is a one-time requirement.

SQL> exec dbms_stats.create_stat_table('TESTUSER', 'SAVED_STATS');


PL/SQL procedure successfully completed.


Now, when we gather our schema stats, we just tell Oracle about our stats table so that it can save the existing stats. (Note that any calls to DBMS_STATS should be on one line, but I've broken it up here for readability)

SQL>  exec dbms_stats.gather_schema_stats(ownname=>'TESTUSER',
estimate_percent=>10,
statown=>'TESTUSER',
stattab=>'SAVED_STATS',
statid=>'PREVIOUS');


PL/SQL procedure successfully completed.


Let's check that we now have some stats on TEST_TAB. Note that NUM_ROWS is not exactly one million - I estimated.

SQL> select table_name, num_rows from user_tables;


TABLE_NAME NUM_ROWS
------------------------------ ----------
SAVED_STATS 0
TEST_TAB 998780
TEST_TAB1 65543840
TEST_TAB2 65532810


You can look at the stats table as a normal table, but you need to deciper the information a little. We can see here that there are saved stats for two tables (TEST_TAB1 and TEST_TAB2 that originally had stats)

SQL> select statid, type, count(*)
2 from saved_stats
3 group by statid, type;


STATID T COUNT(*)
------------------------------ - ----------
PREVIOUS C 6
PREVIOUS T 2


It's not rocket science to view the contents of the stats table directly, but Oracle could change the format over time, so it's probably best to use the supplied procedures for accessing the information. Here, I'm going to look at the saved stats for TEST_TAB1.

SQL> variable my_num_rows number;
SQL> variable my_num_blks number;
SQL> variable my_avg_row_len number;
SQL> exec dbms_stats.get_table_stats(ownname=>'TESTUSER',
tabname=>'TEST_TAB1',
stattab=>'SAVED_STATS',
statown=>'TESTUSER',
statid=>'PREVIOUS',
numrows=>:my_num_rows,
numblks=>:my_num_blks,
avgrlen=>:my_avg_row_len);


PL/SQL procedure successfully completed.


SQL> print my_num_rows


MY_NUM_ROWS
-----------
65536600


Now I'll truncate it so when I next gather stats, they'll reflect that.

SQL> truncate table test_tab1;


Table truncated.


SQL> exec dbms_stats.gather_schema_stats(ownname=>'TESTUSER',
estimate_percent=>10,
statown=>'TESTUSER',
stattab=>'SAVED_STATS',
statid=>'PREVIOUS');


PL/SQL procedure successfully completed.


SQL> select table_name, num_rows from user_tables;


TABLE_NAME NUM_ROWS
------------------------------ ----------
SAVED_STATS 34
TEST_TAB 999480
TEST_TAB1 0
TEST_TAB2 65554250


So now the stats on TEST_TAB1 show that there are zero rows in that table, but we know it used to have 65 million rows in it (but we might not have known that if we were called out in the middle of the night ;-) )


Lets have another look at the stats table. Note that there are saved stats for four tables now, saved_stats, test_tab, test_tab1 and test_tab2.

SQL> select statid, type, count(*)
2 from saved_stats
3 group by statid, type;


STATID T COUNT(*)
------------------------------ - ----------
PREVIOUS C 34
PREVIOUS T 4


So hopefully, we'll be able to look at the old stats for TEST_TAB1

SQL> exec dbms_stats.get_table_stats(ownname=>'TESTUSER', 
tabname=>'TEST_TAB1',
stattab=>'SAVED_STATS',
statown=>'TESTUSER',
statid=>'PREVIOUS',
numrows=>:my_num_rows,
numblks=>:my_num_blks,
avgrlen=>:my_avg_row_len);


PL/SQL procedure successfully completed.


SQL> print my_num_rows


MY_NUM_ROWS
-----------
0


Even though that we know the current stats are an accurate reflection of test_tab1 in this forced example, let's say that we want to put the previous stats back again so that we get the previous execution plan. We just need to import them.

SQL> exec dbms_stats.import_schema_stats(ownname=>'TESTUSER',
stattab=>'SAVED_STATS',
statown=>'TESTUSER',
statid=>'PREVIOUS');


PL/SQL procedure successfully completed.


SQL> select table_name, num_rows from user_tables;


TABLE_NAME NUM_ROWS
------------------------------ ----------
SAVED_STATS 0
TEST_TAB 998780
TEST_TAB1 65543840
TEST_TAB2 65532810


More information

Posted by Doug Burns Comments: (4) Trackback: (1)

Trackbacks
Trackback specific URI for this entry

PingBack
Weblog: jonathanlewis.wordpress.com
Tracked: Jan 26, 18:46

Comments
Display comments as (Linear | Threaded)

#1 - Adrian Taplin 2012-07-11 17:28 - (Reply)

Dear Doug,

a question for you. We have an old database, 9i, its not been analysed for about a year and its running like a dog. The app support team want a nice warm feeling before I analyse the whole of a schema. So how do I save the existing old stats? Can I do it via the exp utility or some other way?

Ta

Adrian ( Ex Scottish Water, or as my Uncle refers to it .... Whisky! He's 90 on Sunday! )

#2 - Doug Burns said:
2012-07-12 20:18 - (Reply)

Hi Adrian,

You just use the approach in this post. Create a stats table and just supply the details when you gather stats and the existing stats will be saved in the stats table.

Cheers,

Doug

#3 - Paul 2013-01-02 14:59 - (Reply)

Hi Doug

thanks for the info and apologies for query on an old post

If we have a regular export_schema_stats in place without a statid value does Oracle overwrite the contents on the STATTAB or does it generates its own STATID?

How does suebsequent IMPORT_SCHEMA_STATS without a STATID use the latest stats in our STATTAB?

Thanks

PMcM

#4 - Doug Burns said:
2013-01-03 10:57 - (Reply)

Hi Paul,

From memory (I don't have a copy of 9i to hand), if you don't specify your own STATID, you would keep overwriting the same set of stats.

But it also occurs to me to ask why you wouldn't just specify the STATID if you want to be able to restore different versions of stats?

Also, if you really want to do anything reasonably clever with stats, I can't help thinking you'd be better off on at least 10g

Cheers,

Doug

Cheers,

Doug


Add Comment

Standard emoticons like :-) and ;-) are converted to images.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications.
BBCode format allowed
 
 

Statistics on Partitioned Tables

Contents

Part 1 - Default options - GLOBAL AND PARTITION
Part 2 - Estimated Global Stats
Part 3 - Stats Aggregation Problems I
Part 4 - Stats Aggregation Problems II
Part 5 - Minimal Stats Aggregation
Part 6a - COPY_TABLE_STATS - Intro
Part 6b - COPY_TABLE_STATS - Mistakes
Part 6c - COPY_TABLE_STATS - Bugs and Patches
Part 6d - COPY_TABLE_STATS - A Light-bulb Moment
Part 6e - COPY_TABLE_STATS - Bug 10268597

Comments

Doug Burns about 10053 Trace Files - Different Plan in Different Environments
Tue, 02.04.2013 08:57
You're welcome. Now I just nee d to pull my finger out and ac tually come up [...]
Howard Rogers about 10053 Trace Files - Different Plan in Different Environments
Mon, 01.04.2013 23:08
Makes a big difference, so tha nks for that! With two brow ser windows, o [...]
stelioscharalambides.com about 10053 Trace Files
Sat, 30.03.2013 16:28

Upcoming Presentations

Bookmark

Open All | Close All

Syndicate This Blog

  • XML RSS 2.0 feed
  • ATOM/XML ATOM 1.0 feed
  • XML RSS 2.0 Comments
  • Feedburner Feed

Powered by

Serendipity PHP Weblog

Show tagged entries

xml 11g
xml ACE
xml adaptive thresholds
xml ASH
xml Audit Vault
xml AWR
xml Blogging
xml conferences
xml Cuddly Toys
xml Database Refresh
xml DBMS_STATS
xml Direct Path Reads
xml Fun
xml grid control
xml hotsos 2010
xml listener
xml Locking
xml oow
xml oow2009
xml optimiser
xml OTN
xml Parallel
xml Partitions
xml Patching
xml swingbench
xml The Reality Gap
xml time matters
xml ukoug
xml ukoug2009
xml Unix/Shell
xml Useful Links

Disclaimer

For the avoidance of any doubt, all views expressed here are my own and not those of past or current employers, clients, friends, Oracle Corporation, my Mum or, indeed, Flatcat. If you want to sue someone, I suggest you pick on Tigger, but I hope you have a good lawyer. Frankly, I doubt any of the former agree with my views or would want to be associated with them in any way.

Design by Andreas Viklund | Conversion to s9y by Carl