-- +----------------------------------------------------------------------------+
-- |                          Jeffrey M. Hunter                                 |
-- |                      jhunter@idevelopment.info                             |
-- |                         www.idevelopment.info                              |
-- |----------------------------------------------------------------------------|
-- |      Copyright (c) 1998-2012 Jeffrey M. Hunter. All rights reserved.       |
-- |----------------------------------------------------------------------------|
-- | DATABASE : Oracle                                                          |
-- | FILE     : sp_purge_30_days_9i.sql                                         |
-- | CLASS    : Statspack                                                       |
-- | PURPOSE  : This script is responsible for removing all Statspack snapshot  |
-- |            records older than 30 days. Most of the code contained in this  |
-- |            script is modeled after the Oracle supplied sppurge.sql script  |
-- |            but removes by Snapshot date rather than Snapshot IDs.          |
-- |                                                                            |
-- |            Note that this script only works with Oracle9i.                 |
-- |                                                                            |
-- | NOTE     : As with any code, ensure to test this script in a development   |
-- |            environment before attempting to run it in production.          |
-- +----------------------------------------------------------------------------+

SET LINESIZE  145
SET PAGESIZE  9999
SET FEEDBACK  off
SET VERIFY    off

DEFINE days_to_keep=30

UNDEFINE dbid inst_num hisnapid

WHENEVER SQLERROR EXIT ROLLBACK

SPOOL sp_purge_&days_to_keep._days_9i.lis


prompt 
prompt 
prompt 
prompt +----------------------------------------------------------------------------+
prompt | Get database and instance currently connected to. This will be used later  |
prompt | in the report along with other metadata to lookup snapshots.               |
prompt +----------------------------------------------------------------------------+

SET FEEDBACK off

COLUMN inst_num   FORMAT 99999999999999  HEADING "Instance Num."  NEW_VALUE inst_num
COLUMN inst_name  FORMAT a15             HEADING "Instance Name"  NEW_VALUE inst_name
COLUMN db_name    FORMAT a10             HEADING "DB Name"        NEW_VALUE db_name
COLUMN dbid       FORMAT 9999999999      HEADING "DB Id"          NEW_VALUE dbid

SELECT
    d.dbid             dbid
  , d.name             db_name
  , i.instance_number  inst_num
  , i.instance_name    inst_name
FROM
    v$database d
  , v$instance i
/


VARIABLE dbid        NUMBER;
VARIABLE inst_num    NUMBER;
VARIABLE inst_name   VARCHAR2(20);
VARIABLE db_name     VARCHAR2(20);

BEGIN
  :dbid      :=  &dbid;
  :inst_num  :=  &inst_num;
  :inst_name := '&inst_name';
  :db_name   := '&db_name';
END;
/

SET FEEDBACK on


prompt 
prompt 
prompt 
prompt +----------------------------------------------------------------------------+
prompt | Obtain the MIN and MAX Snapshot IDs to be removed from the range of IDs    |
prompt | order than &days_to_keep days.                                                        |
prompt +----------------------------------------------------------------------------+

SET FEEDBACK off

COLUMN lo_snap    HEADING "Min Snapshot ID"  NEW_VALUE LoSnapId
COLUMN hi_snap    HEADING "Max Snapshot ID"  NEW_VALUE HiSnapId

SELECT  NVL(MAX(snap_id),0)    hi_snap
      , NVL(MIN(snap_id),0)    lo_snap
FROM    stats$snapshot
WHERE   snap_time < (sysdate - &days_to_keep);

VARIABLE lo_snap   NUMBER;
VARIABLE hi_snap   NUMBER;

BEGIN 
  :lo_snap   :=  &losnapid;
  :hi_snap   :=  &hisnapid; 
END;
/


COLUMN l  HEADING 'Low Snap ID'
COLUMN h  HEADING 'High Snap ID'

SELECT
    :lo_snap  l
  , :hi_snap  h
FROM dual;

SET FEEDBACK ON


prompt 
prompt 
prompt 
prompt +----------------------------------------------------------------------------+
prompt | Snapshots that will be removed for this database instance.                 |
prompt +----------------------------------------------------------------------------+

SET FEEDBACK off

COLUMN snap_id    FORMAT 9999990  HEADING 'Snap Id'
COLUMN level      FORMAT 99       HEADING 'Snap|Level'
COLUMN snap_date  FORMAT a21      HEADING 'Snapshot Started'
COLUMN host_name  FORMAT a15      HEADING 'Host'
COLUMN ucomment   format a25      HEADING 'Comment' 

SELECT
    s.snap_id                                      snap_id
  , s.snap_level                                   "level"
  , to_char(s.snap_time,'mm/dd/yyyy HH24:MI:SS')   snap_date
  , di.host_name                                   host_name
  , s.ucomment                                     ucomment
FROM
    stats$snapshot           s
  , stats$database_instance  di
WHERE
      s.dbid              = :dbid
  AND di.dbid             = :dbid
  AND s.instance_number   = :inst_num
  AND di.instance_number  = :inst_num
  AND di.startup_time     = s.startup_time
  AND s.snap_id           < :hi_snap
ORDER BY
    db_name
  , instance_name
  , snap_id
/

SET HEADING off

SELECT 'WARNING: No snapshots found older than &days_to_keep days in STATS$SNAPSHOT'
FROM   dual
WHERE  NOT EXISTS 
       (
         select null
         from   stats$snapshot
         where  instance_number = :inst_num
           and  dbid            = :dbid
           and  snap_id         = :lo_snap
       )
       OR
       NOT EXISTS
       (
         select null
         from   stats$snapshot
         where  instance_number = :inst_num
           and  dbid            = :dbid
          and  snap_id         = :hi_snap
       )
/

SET HEADING on
SET FEEDBACK ON



prompt 
prompt 
prompt 
prompt +----------------------------------------------------------------------------+
prompt | Get begin and end snapshot times - these are used to delete undostat.      |
prompt +----------------------------------------------------------------------------+

SET FEEDBACK OFF

COLUMN btime  NEW_VALUE btime
COLUMN etime  NEW_VALUE etime

SELECT
    b.snap_id
  , TO_CHAR(b.snap_time, 'YYYYMMDD HH24:MI:SS') btime
FROM
    stats$snapshot b
WHERE
      b.snap_id         = :lo_snap
  AND b.dbid            = :dbid
  AND b.instance_number = :inst_num;


SELECT
    e.snap_id
  , TO_CHAR(e.snap_time, 'YYYYMMDD HH24:MI:SS') etime
FROM stats$snapshot e
WHERE
      e.snap_id         = :hi_snap
  AND e.dbid            = :dbid
  AND e.instance_number = :inst_num;

VARIABLE btime   VARCHAR2(25);
VARIABLE etime   VARCHAR2(25);

BEGIN 
  :btime     :=  '&btime';
  :etime     :=  '&etime';
END;
/

SET FEEDBACK on


prompt 
prompt 
prompt 
prompt +----------------------------------------------------------------------------+
prompt | Deleting snapshots older than &days_to_keep days.
prompt | Deleting snapshots &&losnapid - &&hisnapid.
prompt +----------------------------------------------------------------------------+

DELETE FROM stats$snapshot
 WHERE instance_number = :inst_num
   AND dbid            = :dbid
   AND snap_id between :lo_snap and :hi_snap;


prompt 
prompt 
prompt 
prompt +----------------------------------------------------------------------------+
prompt | Delete any dangling SQLtext. The following statement deletes any dangling  |
prompt | SQL statements which are no longer referred to by ANY snapshots. By        |
prompt | default, Oracle comments this statement out as it can be very resource     |
prompt | intensive.                                                                 |
prompt +----------------------------------------------------------------------------+

SET FEEDBACK off

ALTER SESSION SET hash_area_size=1048576;

COLUMN last_snap_id  HEADING 'Last Snap ID'
COLUMN count         HEADING 'Count'

SELECT --+ index_ffs(st)
    last_snap_id
  , count(*)      count
FROM
    stats$sqltext st
WHERE
    (   hash_value
      , text_subset
    )
    NOT IN
    (  select --+ hash_aj full(ss) no_expand 
           hash_value
         , text_subset
       from stats$sql_summary ss
       where ( (
                 snap_id     < :lo_snap
                 or
                 snap_id     > :hi_snap
               )
               and dbid            = :dbid
               and instance_number = :inst_num
             )
             or
             (
               dbid            != :dbid
               or
               instance_number != :inst_num
             )
    )
GROUP BY
    last_snap_id;

SET FEEDBACK on

DELETE --+ index_ffs(st) 
FROM  stats$sqltext st
WHERE
    (   hash_value
      , text_subset
    )
    NOT IN
    (  select --+ hash_aj full(ss) no_expand 
           hash_value
         , text_subset
       from stats$sql_summary ss
       where ( (
                 snap_id     < :lo_snap
                 or
                 snap_id     > :hi_snap
               )
               and dbid            = :dbid
               and instance_number = :inst_num
             )
             or
             (
               dbid            != :dbid
               or
               instance_number != :inst_num
             )
    );


SET FEEDBACK on

prompt 
prompt 
prompt 
prompt +----------------------------------------------------------------------------+
prompt | (OPTIONAL) - STATS$SEG_STAT_OBJ delete statement                           |
prompt +----------------------------------------------------------------------------+

DELETE --+ index_ffs(sso)
FROM  stats$seg_stat_obj sso
WHERE (   dbid
        , dataobj#
        , obj#
      )
      NOT IN
      (
        select --+ hash_aj full(ss) no_expand
            dbid
          , dataobj#
          , obj#
        from
            stats$seg_stat ss
        where ( ( snap_id     < :lo_snap
                  or
                  snap_id     > :hi_snap
                 )
                 and dbid            = :dbid
                 and instance_number = :inst_num
              )
              or
              ( dbid            != :dbid
                or
                instance_number != :inst_num
              )
      );


prompt 
prompt 
prompt 
prompt +----------------------------------------------------------------------------+
prompt | Delete from stats$undostat                                                 |
prompt | Undostat rows that cover the snap times                                    |
prompt +----------------------------------------------------------------------------+

COLUMN dbid             HEADING 'DB Id'
COLUMN instance_number  HEADING 'Instance Number'
COLUMN snap_id          HEADING 'Snap ID'
COLUMN begin_time       HEADING 'Begin Time'
COLUMN end_time         HEADING 'End Time'

SELECT
    dbid
  , instance_number
  , snap_id
  , TO_CHAR(begin_time, 'YYYYMMDD HH24:MI:SS') begin_time
  , TO_CHAR(end_time,   'YYYYMMDD HH24:MI:SS') end_time
FROM
    stats$undostat us
WHERE
      dbid            = :dbid
  AND instance_number = :inst_num
  AND end_time        <  to_date(:etime, 'YYYYMMDD HH24:MI:SS')
ORDER BY
    snap_id;


DELETE from stats$undostat us
 WHERE dbid            = :dbid
   AND instance_number = :inst_num
   AND end_time        <  to_date(:etime, 'YYYYMMDD HH24:MI:SS');


prompt 
prompt 
prompt 
prompt +----------------------------------------------------------------------------+
prompt | Delete from stats$database_instance                                        |
prompt | Dangling database instance rows for that startup time                      |
prompt +----------------------------------------------------------------------------+

COLUMN dbid             HEADING 'DB Id'
COLUMN instance_number  HEADING 'Instance Number'
COLUMN startup_time     HEADING 'Startup Time'
COLUMN snap_id          HEADING 'Snap ID'

SELECT
    dbid
  , instance_number
  , TO_CHAR(startup_time, 'DD-MON-YYYY HH24:MI:SS') startup_time
  , snap_id
FROM
    stats$database_instance di
WHERE
      instance_number = :inst_num
  AND dbid            = :dbid
  AND NOT EXISTS (select 1
                  from   stats$snapshot s
                  where  s.dbid            = di.dbid
                    and  s.instance_number = di.instance_number
                    and  s.startup_time    = di.startup_time)
ORDER BY
    snap_id;


DELETE from stats$database_instance di
 WHERE instance_number = :inst_num
   AND dbid            = :dbid
   AND NOT EXISTS (select 1
                   from   stats$snapshot s
                   where  s.dbid            = di.dbid
                     and  s.instance_number = di.instance_number
                     and  s.startup_time    = di.startup_time);

prompt 
prompt 
prompt 
prompt +----------------------------------------------------------------------------+
prompt | Delete from stats$statspack_parameter                                      |
prompt | Dangling statspack parameter rows for the database / instance              |
prompt +----------------------------------------------------------------------------+

COLUMN dbid             HEADING 'DB Id'
COLUMN instance_number  HEADING 'Instance Number'
COLUMN session_id       HEADING 'Session ID'
COLUMN snap_level       HEADING 'Snap Level'

SELECT
    dbid
  , instance_number
  , session_id
  , snap_level
FROM
    stats$statspack_parameter sp
WHERE
      instance_number = :inst_num
  AND dbid            = :dbid
  AND NOT EXISTS (select 1
                  from   stats$snapshot s
                  where  s.dbid            = sp.dbid
                    and  s.instance_number = sp.instance_number);


DELETE from stats$statspack_parameter sp
 WHERE instance_number = :inst_num
   AND dbid            = :dbid
   AND NOT EXISTS (select 1
                   from   stats$snapshot s
                   where  s.dbid            = sp.dbid
                     and  s.instance_number = sp.instance_number);


SPOOL off

COMMIT;

EXIT

