Skip to content

DBAzine.com

Sections
Personal tools
You are here: Home » 10g » Oracle10g Articles » Oracle10g SQL Time-series Tuning
Who Are You?
I am a:
Mainframe True Believer
Distributed Fast-tracker

[ Results | Polls ]
Votes : 3548
 

Oracle10g SQL Time-series Tuning

by Donald K. Burleson

This article is a brief overview of SQL tuning in Oracle10g, with a focus on the new 10g SQL tuning features. The main new features of SQL tuning in Oracle10g include the following:

      • Oracle10g now automatically collects and refreshes schema statistics using the dbms_stats package, and histogram collection is now easily automated with dbms_stats
      • One common cause of sub-optimal SQL is missing materialized view and indexes, especially function-based indexes. The SQLTuning Advisor and SQLAccess Advisor provide an easy method for identifying and tuning SQL with sub-optimal execution plans.
      • SQL Profiles are a great improvement of Optimizer Plan Stability (stored outlines).
      • The new dba_hist tables contain a wealth of historical information about historical SQL execution statistics.
      • Time-series analysis of object usage within SQL can give us important insights into holistic tuning for SQL statements.

Let’s dive into the most important of all of the Oracle 10g tuning features, the automated Workload Repository, and see how we can perform time-series SQL tuning.

AWR and SQL Tuning

The new Oracle10g AWR tables contain super-useful information about the time-series execution plans for SQL statements and this repository can be used to display details about the frequency of usage for table and indexes. The following are AWR tables for time-series SQL tuning (refer to figure 1):

      • dba_hist_sqlstat 
• dba_hist_sql_summary
• dba_hist_sql_workarea
• dba_hist_sql_plan
• dba_hist_sql_workarea_histogram


Figure 1: The dba_hist views for SQL tuning.

When we get started with time-series SQL tuning, we have to remember the basic relationships between database objects and SQL statements (refer to figure 2):

      • Each SQL statement may generate many access plans — As we know from our discussion of dynamic sampling and dbms_stats, the execution plans for SQL statements will change over time to accommodate changes in the data they access. It’s important to understand how and when a frequently executed SQL statement changes its access plan.
      • Each object is accessed by many access plans — In most OLTP systems, tables and indexes show repeating patterns of usage, and we can see clear patterns when we average object access by day-of-the-week and hour-of-the-day.

Figure 2: Time-series relationships between SQL and database objects.

As we see, there is a many-to-many relationship between any given SQL statement and the tables they access. Once we understand this fundamental relationship, we can begin to use the AWR tables to perform time-based SQL tuning.

These simple tables represent a revolution in Oracle SQL tuning, and we can now employ time-series techniques to optimizer SQL with better results than ever before. Let’s take a closer look at these views.

dba_hist_sqlstat

This view is very similar to the v$sql view, but it contains important SQL metrics for each snapshot. These include important delta (change) information on disk reads and buffer gets, as well as time-series delta information on application, I/O, and concurrency wait times.

--*************************************************
--   Copyright © 2004 by Rampant TechPress Inc.
--   Free for non-commercial use! 
--   To license, e-mail info@rampant.cc
-- ************************************************


col c1 heading ‘Begin|Interval|time’    format a8
col c2 heading ‘SQL|ID’                 format a13
col c3 heading ‘Exec|Delta’             format 9,999
col c4 heading ‘Buffer|Gets|Delta’      format 9,999
col c5 heading ‘Disk|Reads|Delta’       format 9,999
col c6 heading ‘IO Wait|Delta’          format 9,999
col c7 heading ‘Application|Wait|Delta’ format 9,999
col c8 heading ‘Concurrency|Wait|Delta’ format 9,999


break on c1


select
  to_char(s.begin_interval_time,’mm-dd hh24’)  c1,
  sql.sql_id               c2,
  sql.executions_delta     c3,
  sql.buffer_gets_delta    c4,
  sql.disk_reads_delta     c5,
  sql.iowait_delta         c6,
  sql.apwait_delta         c7,
  sql.ccwait_delta         c8
from
   dba_hist_sqlstat        sql,
   dba_hist_snapshot         s
where
   s.snap_id = sql.snap_id
order by
   c1,
   c2
;

Here is a sample of the output. This is very important because we can see the changes in SQL execution over time periods. For each snapshot period, we see the change in the number of times the SQL was executed as well as important performance information about the performance of the statement.

Begin                         Buffer   Disk         Application Concurrency
Interval SQL             Exec   Gets  Reads IO Wait        Wait        Wait
time     ID             Delta  Delta  Delta   Delta       Delta       Delta
-------- ------------- ------ ------ ------ ------- ----------- -----------
10-10 16 0sfgqjz5cs52w     24     72     12       0 3           0
         1784a4705pt01      1    685      6       0 17           0
         19rkm1wsf9axx     10     61      4       0 0           0
         1d5d88cnwxcw4     52    193      4       6           0           0
         1fvsn5j51ugz3      4      0      0 0 0           0
         1uym1vta995yb      1    102      0 0 0           0
         23yu0nncnp8m9     24     72      0 0 6           0
         298ppdduqr7wm      1      3      0       0           0           0
         2cpffmjm98pcm      4     12      0       0           0           0
         2prbzh4qfms7u      1  4,956     19       1          34           5


10-10 17 0sfgqjz5cs52w     30     90      1       0           0           0
         19rkm1wsf9axx     14     88      0       0           0           0
         1fvsn5j51ugz3      4      0      0       0           0           0
         1zcdwkknwdpgh      4      4      0       0           0           0
         23yu0nncnp8m9     30     91      0       0           0           5
         298ppdduqr7wm      1      3      0       0           0           0
         2cpffmjm98pcm      4     12      0       0           0           0
         2prbzh4qfms7u      1  4,940     20       0           0           0
         2ysccdanw72pv     30     60      0       0           0           0
         3505vtqmvvf40      2    321      5       1           0           0

This report is especially useful because we can track the logical I/O (buffer gets) vs. physical I/O for each statement over time; this provides important information about the behavior of the SQL statement.

This output gives us a quick overview of the top SQL statements during any AWR snapshot period and shows how their behavior has changed since the last snapshot period. Detecting changes in the behavior of commonly executed SQL statements is the key to time-series SQL tuning.

Note that we can easily add a WHERE clause to the above script and plot the I/O changes over time:

< awr_sqlstat_deltas_detail.sql

--*************************************************
--   Copyright © 2004 by Rampant TechPress Inc.
--   Free for non-commercial use!  
--   To license, e-mail info@rampant.cc
-- ************************************************

col c1 heading ‘Begin|Interval|time’    format a8
col c2 heading ‘Exec|Delta’             format 999,999
col c3 heading ‘Buffer|Gets|Delta’      format 999,999
col c4 heading ‘Disk|Reads|Delta’       format 9,999
col c5 heading ‘IO Wait|Delta’          format 9,999
col c6 heading ‘App|Wait|Delta’         format 9,999
col c7 heading ‘Cncr|Wait|Delta’        format 9,999
col c8 heading ‘CPU|Time|Delta’         format 999,999
col c9 heading ‘Elpsd|Time|Delta’       format 999,999


accept sqlid prompt ‘Enter SQL ID: ‘

ttitle ‘time series execution for|&sqlid’

break on c1 

select
  to_char(s.begin_interval_time,’mm-dd hh24’)  c1,
  sql.executions_delta     c2,
  sql.buffer_gets_delta    c3,
  sql.disk_reads_delta     c4,
  sql.iowait_delta         c5,
  sql.apwait_delta         c6,
  sql.ccwait_delta         c7,
  sql.cpu_time_delta       c8,
  sql.elapsed_time_delta   c9
from
   dba_hist_sqlstat        sql,
   dba_hist_snapshot         s
where
   s.snap_id = sql.snap_id
and
   sql_id = ‘&sqlid’
order by
   c1
;

Here we can see the changes to the execution of a frequently used SQL statement and how its behavior changes over time:

Begin               Buffer   Disk            App   Cncr      CPU    Elpsd
Interval     Exec     Gets  Reads IO Wait   Wait   Wait     Time     Time
time        Delta    Delta  Delta   Delta  Delta  Delta    Delta    Delta
-------- -------- -------- ------ ------- ------ ------ -------- --------
10-14 10      709    2,127      0       0      0      0  398,899  423,014
10-14 11      696    2,088      0       0      0      0  374,502  437,614
10-14 12      710    2,130      0       0      0      0  384,579  385,388
10-14 13      693    2,079      0       0      0      0  363,648  378,252
10-14 14      708    2,124      0       0      0      0  373,902  373,902
10-14 15      697    2,091      0       0      0      0  388,047  410,605
10-14 16      707    2,121      0       0      0      0  386,542  491,830
10-14 17      698    2,094      0       0      0      0  378,087  587,544
10-14 18      708    2,124      0       0      0      0  376,491  385,816
10-14 19      695    2,085      0       0      0      0  361,850  361,850
10-14 20      708    2,124      0       0      0      0  368,889  368,889
10-14 21      696    2,088      0       0      0      0  363,111  412,521
10-14 22      709    2,127      0       0      0      0  369,015  369,015
10-14 23      695    2,085      0       0      0      0  362,480  362,480
10-15 00      709    2,127      0       0      0      0  368,554  368,554
10-15 01      697    2,091      0       0      0      0  362,987  362,987
10-15 02      696    2,088      0       0      0      2  361,445  380,944
10-15 03      708    2,124      0       0      0      0  367,292  367,292
10-15 04      697    2,091      0       0      0      0  362,279  362,279
10-15 05      708    2,124      0       0      0      0  367,697  367,697
10-15 06      696    2,088      0       0      0      0  361,423  361,423
10-15 07      709    2,127      0       0      0      0  374,766  577,559
10-15 08      697    2,091      0       0      0      0  364,879  410,328

In the preceding listing, we see how the number of executions varies over time.

But AWR has lots more useful information. Let’s now look at the dba_hist_sql_plan table.

dba_hist_sql_plan

The dba_hist_sql_plan table contains time-series data about each object (table, index, view) involved in the query. The important columns include the cost, cardinality, cpu_cost, io_cost, and temp_space required for the object. The sample query below retrieves SQL statements that have high query execution cost identified by Oracle optimizer.

But there is a lot more information in dba_hist_sql_plan that is useful. The query below will extract important costing information for all objects involved in each query (SYS objects are not counted).

< awr_sql_object_char.sql


--*************************************************
-- Copyright © 2004 by Rampant TechPress Inc.
-- Free for non-commercial use!
-- To license, e-mail info@rampant.cc
-- ************************************************


col c1 heading ‘Owner’              format a13
col c2 heading ‘Object|Type’        format a15


col c3 heading ‘Object|Name’        format a25
col c4 heading ‘Average|CPU|Cost’   format 9,999,999
col c5 heading ‘Average|IO|Cost’    format 9,999,999


break on c1 skip 2
break on c2 skip 2


select
  p.object_owner    c1,
  p.object_type     c2,
  p.object_name     c3,
  avg(p.cpu_cost)   c4,
  avg(p.io_cost)    c5
from
  dba_hist_sql_plan p
where
        p.object_name is not null
    and
        p.object_owner <> 'SYS'
group by
  p.object_owner,
  p.object_type,
  p.object_name
order by
1,2,4 desc
;

Here is a sample of the output. Here we see the average CPU and I/O costs for all objects that participate in queries, over time periods.

                                                           Average    Average
              Object          Object                           CPU         IO
Owner         Type            Name                            Cost       Cost
------------- --------------- ------------------------- ---------- ----------
OLAPSYS       INDEX           CWM$CUBEDIMENSIONUSE_IDX         200          0


OLAPSYS       INDEX (UNIQUE)  CWM$DIMENSION_PK
OLAPSYS                       CWM$CUBE_PK                    7,321          0
OLAPSYS                       CWM$MODEL_PK                   7,321          0


OLAPSYS       TABLE           CWM$CUBE                       7,911          0
OLAPSYS                       CWM$MODEL                      7,321          0
OLAPSYS                       CWM2$CUBE                      7,121          2
OLAPSYS                       CWM$CUBEDIMENSIONUSE             730          0


PERFSTAT      INDEX (UNIQUE)  STATS$TIME_MODEL_STATNAME     39,242          2


PERFSTAT                      STATS$SYSSTAT_PK              21,564          2
PERFSTAT                      STATS$SGASTAT_U               21,442          2
PERFSTAT                      STATS$SQL_SUMMARY_PK          16,842          2
PERFSTAT                      STATS$SQLTEXT_PK              14,442          1
PERFSTAT                      STATS$IDLE_EVENT_PK            8,171          0


PERFSTAT      TABLE           STATS$SYSSTAT              5,571,375         24
PERFSTAT                      STATS$FILE_HISTOGRAM       1,373,396          5
PERFSTAT                      STATS$SYSTEM_EVENT           996,571          6
PERFSTAT                      STATS$LATCH                  462,161          5
PERFSTAT                      STATS$SQL_SUMMARY            440,038          7
PERFSTAT                      STATS$PARAMETER              361,439          5
PERFSTAT                      STATS$FILESTATXS             224,227          3
PERFSTAT                      STATS$WAITSTAT               144,554          3
PERFSTAT                      STATS$TEMP_HISTOGRAM         126,304          3
PERFSTAT                      STATS$LIBRARYCACHE           102,846          3
PERFSTAT                      STATS$TEMPSTATXS              82,762          3
PERFSTAT                      STATS$SGASTAT                 51,807          5
PERFSTAT                      STATS$SQLTEXT                 17,781          2
PERFSTAT                      STATS$SQL_PLAN_USAGE               0          2


SPV           INDEX (UNIQUE)  WSPV_REP_PK                    7,321          0
SPV                           SPV_ALERT_DEF_PK               7,321          0


SPV           TABLE           WSPV_REPORTS                 789,052         28
SPV                           SPV_MONITOR                   54,092          3
SPV                           SPV_SAVED_CHARTS              38,337          3
SPV                           SPV_DB_LIST                   37,487          3
SPV                           SPV_SCHED                     35,607          3
SPV                           SPV_FV_STAT                   35,607          3
SPV                           SPV_ALERT_DEF                 15,868          1
SPV                           SPV_BASELINES                  7,121          2
SPV                           SPV_ALERT_HISTORY              7,121          2
SPV                           SPV_STORED_SNAP_RANGES         7,121          2

We can now easily change this script to allow us to enter a table name and see changes in access details over time:

< awr_sql_object_char_detail.sql


--*************************************************
-- Copyright © 2004 by Rampant TechPress Inc.
-- Free for non-commercial use!
-- To license, e-mail info@rampant.cc
-- ************************************************


accept tabname prompt ‘Enter Table Name:’


col c0 heading ‘Begin|Interval|time’ format a8
col c1 heading ‘Owner’               format a10
col c2 heading ‘Object|Type’         format a10
col c3 heading ‘Object|Name’         format a15
col c4 heading ‘Average|CPU|Cost’    format 9,999,999
col c5 heading ‘Average|IO|Cost’     format 9,999,999


break on c1 skip 2
break on c2 skip 2


select
  to_char(sn.begin_interval_time,'mm-dd hh24') c0,
  p.object_owner                               c1,
  p.object_type                                c2,
  p.object_name                                c3,
  avg(p.cpu_cost)                              c4,
  avg(p.io_cost)                               c5
from
  dba_hist_sql_plan p,
  dba_hist_sqlstat  st,
  dba_hist_snapshot sn
where
  p.object_name is not null
and
    p.object_owner <> 'SYS'
and
   p.object_name = 'STATS$SYSSTAT'
and
  p.sql_id = st.sql_id
and
  st.snap_id = sn.snap_id
group by
  to_char(sn.begin_interval_time,'mm-dd hh24'),
  p.object_owner,
  p.object_type,  p.object_name
order by
  1,2,3 desc
;

Here we can see changes in the table’s access patterns over time, a very useful feature:

Begin                                             Average    Average
Interval            Object     Object                 CPU         IO
time     Owner      Type       Name                  Cost       Cost
-------- ---------- ---------- --------------- ---------- ----------
10-25 17 PERFSTAT   TABLE      STATS$SYSSTAT       28,935          3
10-26 15 PERFSTAT              STATS$SYSSTAT       28,935          3
10-27 18 PERFSTAT              STATS$SYSSTAT    5,571,375         24
10-28 12 PERFSTAT              STATS$SYSSTAT       28,935          3

Now that we see the important table structures, let’s examine how we can get spectacular reports from this AWR data.

Viewing Table and Index Access with AWR

One of the problems in Oracle9i was the single bit-flag that was used to monitor index usage. You could set the flag with the alter index xxx monitoring usage command, and see if the index was accessed by querying the v$object_usage view.

As we recall, the goal of any index access is to use the most selective index for a query, the one that produces the smallest number of rows. The Oracle data dictionary is usually quite good at this, but it is up to you to define the index. Missing function-based indexes are a common source of sub-optimal SQL execution because Oracle will not use an indexed column unless the WHERE clause matches the index column exactly.

Let’s look at the awr_sql_index.sql script that exposes the cumulative usage of database indexes:

< awr_sql_index.sql


--*************************************************
-- Copyright © 2004 by Rampant TechPress Inc.
-- Free for non-commercial use!
-- To license, e-mail info@rampant.cc
-- ************************************************


col c0 heading ‘Begin|Interval|time’ format a8
col c1 heading ‘Index|Name’          format a20
col c2 heading ‘Disk|Reads’          format 99,999,999
col c3 heading ‘Rows|Processed’      format 99,999,999


select
  to_char(s.begin_interval_time,'mm-dd hh24')  c0,
  p.object_name               c1,
  sum(t.disk_reads_total)     c2,
  sum(t.rows_processed_total) c3
from
       dba_hist_sql_plan p,
       dba_hist_sqlstat  t,
       dba_hist_snapshot s
where
       p.sql_id = t.sql_id
   and
       t.snap_id = s.snap_id
   and
       p.object_type like '%INDEX%'
group by
       to_char(s.begin_interval_time,'mm-dd hh24'),
       p.object_name
order by
       c0,c1,c2 desc
;

Here is a sample of the output:

Begin
Interval Index                       Disk        Rows
time     Name                       Reads   Processed
-------- -------------------- ----------- -----------
10-14 12 I_CACHE_STATS_1                          114
10-14 12 I_COL_USAGE$                 201       8,984
10-14 12 I_FILE1                        2           0
10-14 12 I_IND1                        93         604
10-14 12 I_JOB_NEXT                     1     247,816
10-14 11 I_KOPM1                        4       2,935
10-14 11 I_MON_MODS$_OBJ               12      28,498
10-14 11 I_OBJ1                    72,852         604
10-14 11 I_PARTOBJ$                    93         604
10-14 11 I_SCHEDULER_JOB2               4           0
1014 11 SYS_C002433                  302       4,629
10-14 11 SYS_IOT_TOP_8540               0      75,544
10-14 11 SYS_IOT_TOP_8542               1       4,629
10-14 11 WRH$_DATAFILE_PK               2           0
10-14 10 WRH$_SEG_STAT_OBJ_PK          93         604
10-14 10 WRH$_TEMPFILE_PK                           0
10-14 10 WRI$_ADV_ACTIONS_PK           38       1,760

You can also use the dba_hist_sql_plan table to gather counts about the frequency of participation of objects inside queries.

< awr_sql_object_freq.sql


--*************************************************
-- Copyright © 2004 by Rampant TechPress Inc.
-- Free for non-commercial use!
-- To license, e-mail info@rampant.cc
-- ************************************************


col c1 heading ‘Object|Name’         format a30
col c2 heading ‘Operation’           format a15
col c3 heading ‘Option’              format a15
col c4 heading ‘Object|Count’        format 999,999


break on c1 skip 2
break on c2 skip 2


select
  p.object_name c1,
  p.operation   c2,
  p.options     c3,
  count(1)      c4
from
  dba_hist_sql_plan p,
  dba_hist_sqlstat s
where
      p.object_owner <> 'SYS'
  and
      p.sql_id = s.sql_id
group by
  p.object_name, p.operation, p.options
order by
  1,2,3;

Here is the output that shows each table and the resulting access method totals:

Object                                                           Object
Name                           Operation       Option            Count
------------------------------ --------------- --------------- --------
CUSTOMER                       TABLE ACCESS    FULL                 305

CUSTOMER  _CHECK               INDEX           RANGE SCAN             2

CUSTOMER_ORDERS                TABLE ACCESS    BY INDEX ROWID       311
CUSTOMER_ORDERS                                FULL                   1

CUSTOMER_ORDERS_PRIMARY        INDEX           FULL SCAN              2
CUSTOMER_ORDERS_PRIMARY                        UNIQUE SCAN          311
AVAILABILITY_PRIMARY_KEY                       RANGE SCAN             4
CON_UK                                         RANGE SCAN             3
CURRENT_SEVERITY_PRIMARY_KEY                   RANGE SCAN             1

CWM$CUBE                       TABLE ACCESS    BY INDEX ROWID         2
CWM$CUBEDIMENSIONUSE                           BY INDEX ROWID         2


CWM$CUBEDIMENSIONUSE_IDX       INDEX           RANGE SCAN             2
CWM$CUBE_PK                                    UNIQUE SCAN            2
CWM$DIMENSION_PK                               FULL SCAN              2

MGMT_INV_VERSIONED_PATCH       TABLE ACCESS    BY INDEX ROWID         3
MGMT_JOB                                       BY INDEX ROWID       458
MGMT_JOB_EMD_STATUS_QUEUE                      FULL                 181
MGMT_JOB_EXECUTION                             BY INDEX ROWID       456

MGMT_JOB_EXEC_IDX01            INDEX           RANGE SCAN           456

MGMT_JOB_EXEC_SUMMARY          TABLE ACCESS    BY INDEX ROWID       180

MGMT_JOB_EXEC_SUMM_IDX04       INDEX           RANGE SCAN           180



MGMT_JOB_HISTORY               TABLE ACCESS    BY INDEX ROWID         1

MGMT_JOB_HIST_IDX01            INDEX           RANGE SCAN             1
MGMT_JOB_PK                                    UNIQUE SCAN          458


MGMT_METRICS                   TABLE ACCESS    BY INDEX ROWID       180

Using the previous output, we are able to easily monitor object participation (especially indexes) in the SQL queries and the mode in which an object was accessed by Oracle.

We can also count specific access methods and see how they change over time. This is especially important for large-table full-table scans (LTFTS) because they are a common symptom of sub-optimal execution plans (i.e., missing indexes).

Once we are assured that the large-table, full-table scans are legitimate, we must know the times when they are executed so that we can implement selective parallel query, depending on the existing CPU consumption on the server (as we know, OPQ drives up CPU consumption, and should be invoked only when the server can handle the additional load).

< awr_full_table_scans.sql


--*************************************************
-- Copyright © 2004 by Rampant TechPress Inc.
-- Free for non-commercial use!
-- To license, e-mail info@rampant.cc
-- ************************************************


ttile ‘Large Tabe Full-table scans|Per Snapshot Period’


col c1 heading ‘Begin|Interval|time’ format a20
col c4 heading ‘FTS|Count’           format 999,999


break on c1 skip 2
break on c2 skip 2


select
  to_char(sn.begin_interval_time,'yy-mm-dd hh24')  c1,
  count(1)                                         c4
from
   dba_hist_sql_plan p,
   dba_hist_sqlstat  s,
   dba_hist_snapshot sn,
   dba_segments      o
where
   p.object_owner <> 'SYS'
and
   p.object_owner = o.owner
and
   p.object_name = o.segment_name
and
   o.blocks > 1000
and
   p.operation like '%TABLE ACCESS%'
and
   p.options like '%FULL%'
and
   p.sql_id = s.sql_id
and
   s.snap_id = sn.snap_id
group by
  to_char(sn.begin_interval_time,'yy-mm-dd hh24')
order by
  1;

From the following output, we see overall total counts for full-tables experience large-table full-table scans. These scans may be due to a missing index.

Begin
Interval                FTS
time                    Count
-------------------- --------
04-10-18 11                 4
04-10-21 17                 1
04-10-21 23                 2
04-10-22 15                 2
04-10-22 16                 2
04-10-22 23                 2
04-10-24 00                 2
04-10-25 00                 2
04-10-25 10                 2
04-10-25 17                 9
04-10-25 18                 1
04-10-25 21                 1
04-10-26 12                 1
04-10-26 13                 3
04-10-26 14                 3
04-10-26 15                11
04-10-26 16                 4
04-10-26 17                 4
04-10-26 18                 3
04-10-26 23                 2
04-10-27 13                 2
04-10-27 14                 3
04-10-27 15                 4
04-10-27 16                 4
04-10-27 17                 3
04-10-27 18                17
04-10-27 19                 1
04-10-28 12                22
04-10-28 13                 2
04-10-29 13                 9

You can easily plot this data and see the trend for your database (refer to figure 3):

Figure 3: Trends of large-table full-table scans.

Here is an amazing script that will show you the access patterns of usage over time. If you really want to “know your system,” understanding how SQL accesses the tables and indexes in your database can provide you with amazing insights. As we know, the optimal instance configuration for large-table full-table scans is quite different then the configuration for an OLTP databases, and this handy report will quickly identify changes in table access patterns.

< awr_sql_scan_sums.sql



--*************************************************
-- Copyright © 2004 by Rampant TechPress Inc.
-- Free for non-commercial use! 
-- To license, e-mail info@rampant.cc
-- ************************************************

col c1 heading ‘Begin|Interval|Time’          format a20
col c2 heading ‘Large|Table|Full Table|Scans’ format 999,999
col c3 heading ‘Small|Table|Full Table|Scans’ format 999,999
col c4 heading ‘Total|Index|Scans’            format 999,999

select
  f.c1  c1,
  f.c2  c2,
  s.c2  c3,
  i.c2  c4
from
(
select
  to_char(sn.begin_interval_time,'yy-mm-dd hh24')  c1,
  count(1)                           c2
from 
  dba_hist_sql_plan p,
   dba_hist_sqlstat  s,
   dba_hist_snapshot sn,
   dba_segments      o
where
   p.object_owner <> 'SYS'
and
   p.object_owner = o.owner
and
   p.object_name = o.segment_name
and
   o.blocks > 1000
and
   p.operation like '%TABLE ACCESS%'
and
   p.options like '%FULL%'
and
   p.sql_id = s.sql_id
and
   s.snap_id = sn.snap_id
group by
  to_char(sn.begin_interval_time,'yy-mm-dd hh24')
order by
1 ) f,
(
select
  to_char(sn.begin_interval_time,'yy-mm-dd hh24')  c1,
  count(1)                           c2
from
   dba_hist_sql_plan p,
   dba_hist_sqlstat  s,
   dba_hist_snapshot sn,
   dba_segments      o
where
   p.object_owner <> 'SYS'
and
   p.object_owner = o.owner
and
   p.object_name = o.segment_name
and
   o.blocks < 1000
and
   p.operation like '%INDEX%'
and
   p.sql_id = s.sql_id
and
   s.snap_id = sn.snap_id
group by
  to_char(sn.begin_interval_time,'yy-mm-dd hh24')
order by
1 ) s,
(
select
  to_char(sn.begin_interval_time,'yy-mm-dd hh24')  c1,
  count(1)                           c2
from
   dba_hist_sql_plan p,
   dba_hist_sqlstat  s,
   dba_hist_snapshot sn
where
   p.object_owner <> 'SYS'
and 
  p.operation like '%INDEX%'
and
   p.sql_id = s.sql_id
and
   s.snap_id = sn.snap_id
group by
  to_char(sn.begin_interval_time,'yy-mm-dd hh24')
order by
1 ) i
where
      f.c1 = s.c1
   and
      f.c1 = i.c1
;

The sample output looks like:

Begin                     Table      Table    Total
Interval             Full Table Full Table    Index
Time                      Scans      Scans    Scans
-------------------- ---------- ---------- --------
04-10-22 15                   2         19       21
04-10-22 16                              1        1
04-10-25 10                             18       20
04-10-25 17                   9         15       17
04-10-25 18                   1         19       22
04-10-25 21                             19       24
04-10-26 12                             23       28
04-10-26 13                   3         17       19
04-10-26 14                             18       19
04-10-26 15                  11          4        7
04-10-26 16                   4         18       18
04-10-26 17                             17       19
04-10-26 18                   3         17       17
04-10-27 13                   2         17       19
04-10-27 14                   3         17       19
04-10-27 15                   4         17       18
04-10-27 16                             17       17
04-10-27 17                   3         17       20
04-10-27 18                  17         20       22
04-10-27 19                   1         20       26
04-10-28 12                  22         17       20
04-10-28 13                   2         17       17
04-10-29 13                   9         18       19

This is a very important report because it shows you how Oracle is accessing data over time periods. This is especially important because it shows when the database processing modality shifts between OLTP (first_rows index access) to a batch reporting mode (all_rows full scans).

Of course, in a really busy database, you may have concurrent OLTP index access and full-table scans for reports, and it is your job to know the specific times when you shift table access modes, as well as which tables experience the changes.

Conclusion

This is just a taste of the researching that I’m doing for my upcoming book, Oracle 10g Tuning by Rampant TechPress, due out in March 2004. The ability to see SQL executions over time will revolutionize Oracle tuning and allow Oracle professionals to finally understand the dynamic nature of their database.

In later installments, I’ll show you even more advanced time-series SQL tuning scripts, and be sure to check out my DBAzine.com Webinar on “Oracle 10g Time Series SQL Tuning Techniques.”

--

Donald K. Burleson is one of the world’s top Oracle Database experts with more than 20 years of full-time DBA experience. He specializes in creating database architectures for very large online databases and he has worked with some of the world’s most powerful and complex systems. A former Adjunct Professor, Don Burleson has written 15 books, published more than 100 articles in national magazines, serves as Editor-in-Chief of Oracle Internals and edits for Rampant TechPress. Don is a popular lecturer and teacher and is a frequent speaker at Oracle Openworld and other international database conferences. Don’s Web sites include DBA-Oracle, Remote-DBA, Oracle-training, remote support and remote DBA.


Contributors : Donald K. Burleson
Last modified 2005-06-22 12:06 AM
Transaction Management
Reduce downtime and increase repeat sales by improving end-user experience.
Free White Paper
Database Recovery
Feeling the increased demands on data protection and storage requirements?
Download Free Report!
 
 

Powered by Plone