Script to Reorganize Indexes

INDEX REORGANIZATION

Ever since Oracle implemented index rebuild in place option, it has made the job of managing and reorganizing indices easier for the DBA.  If one has disk space available to create a work tablespace for index reorganization, my script uses the rebuild
feature to make the process easier.  It rebuilds the selected indexes on the work tablespace first and then rebuilds them back
to the original or another tablespace, if desired.  There are many options provided in performing this routine task.  The
steps and prompts are as follows:
1) Make the changes you want to the index storage definition
(The only ones you are allowed to change are next, maxextents, pctincrease, freelists, freelist groups)
2) Invoke sqlplus and run this script and provide appropriate information to the prompts:
UNRECOVERABLE? (Y/N) – This tells the alter index rebuild statement to contain the unrecoverable clause.  KNOW THE RISK.
If it fails, you may lose the original index you were trying to rebuild, so you will have to know the index definition in order
to build this manually.

INDEX TABLESPACE TO BE REORGANIZED – Name of the Tablespace to be reorganized.

INDEX TABLESPACE TO USE FOR REORGANIZATION – Name of the work tablespace that will be used for reorganization.

EXTENTS GREATER THAN (0 for all) – Enter the number of extents that an index is in to select for rebuild/reorganization.  Enter 0 for all or any appropriate number like 25.

DELETE DEFAULT STATISTICS? (Y/N) – Alter index rebuild generates default statistics which can cause your rule based optimizer for Oracle Applications and Oracle v7.3 to perform unnecessary full table scans.  If you have Oracle Applications or do not want this default statistics to be collected, enter ‘Y’.

PARALLEL (N is for NOPARALLEL, Number for Degree) – Enter ‘N’ for non parallel rebuild of indexes.  If you have Parallel Query enabled, then you can enter the number of parallel query processes to invoke to rebuild the indexes.  Keep in mind that the initial extent will be the number of parallel query processes times the initial extent of the index.  So you may
want to use the next parameter to alter the initial extent so that this does not cause problems.

ALTER INITIAL EXTENT MODE (N,D,F,K,M) – You can alter the initial extent during the rebuild.  ‘N’ is no alteration. ‘D’ is for dividing, ‘F’ is for multiplying (factoring) the current initial by the number in the next parameter. ‘K’ is for KB in
the next parameter, and ‘M’ is for MB in the next parameter.

ALTER INITIAL EXTENT (1 OR appropriate number) – Enter 1 if ‘N’ was entered in the prior prompt. Enter a number that you want to divide the current initial extent by if ‘D’ was entered above (useful for parallel rebuild so that the initials are not too
large).  Enter a number that you want to increase the initial extent by a factor of (twice the current initial will be ‘F’ in
the prior prompt and ‘2’ for the current prompt).  If you specified ‘K’ or ‘M’, enter the number corresponding to the
kilobytes or megabytes that you want the initial extents to be.
 

ALTER DESTINATION TABLESPACE (N or TABLESPACE NAME) – If you want to change the location of the index to another tablespace than the source tablespace, enter the name of the tablespace.
Otherwise enter ‘N’.  PLEASE GRANT APPROPRIATE QUOTA PRIVILEGE TO THE INDEX OWNER ON THIS TABLESPACE FIRST IF YOU CHOOSE TO RELOCATE.
 

NUMBER OF SECONDS (INTEGER) TO SLEEP BETWEEN STMTS – Number of seconds to wait between execution of the individual alter index statements.  This is required so that the rdbms can perform appropriate cleanups.  Otherwise, you may receive “Unable to allocate…” error.  Typical value of ‘2’ has been sufficient.
 

NUMBER OF SECONDS (INTEGER) TO SLEEP BETWEEN TBLSP – Number of seconds to wait between switching from rebuilding to the work tablespace and rebuilding back to source or alternate destination.  This again allows the rdbms to perform appropriate cleanups.  Typical value is 1 more than above prompt.
 

SCHEMA – The schema which contains the indexes to be reorganized or rebuilt.

SCHEMA PASSWORD (not echoed) – Enter the schema password.

SYSTEM PASSWORD (not echoed) – Enter the system password.
 

3) Generates a spool file.  Please check for errors.  If error was encountered and UNRECOVERABLE was set to ‘N’ you should find the index in the work tablespace.  You have to manually move them, if needed.

4) Shows you the starting count of indexes and the ending count of how many indexes still meet the original selection criteria
and how many are in the destination tablespaces.
 

PLEASE TEST FIRST ON A TEST SYSTEM.

/*
Version: 2.0
Purpose:
    This routine reorganizes indexes for a tablespace
      The flow is as follows:
      Create a tablespace atleast as large as the largest index tablespace
      Make all the allowable changes you want to the storage parameters for
        the current index schema
      Prompts for:
             UNRECOVERABLE? (Y/N)
             INDEX TABLESPACE TO BE REORGANIZED
             INDEX TABLESPACE TO BE USED FOR REORGANIZATION
             SCHEMA
             EXTENTS GREATER THAN TO SELECT FOR REORG
             SCHEMA PASSWORD
             SYSTEM PASSWORD
             DELETE STATISTICS? (Y/N)
             PARALLEL ('N' NOPARALLEL) enter the degree (non zero)
             ALTDEST ('N' NO, Tablespace name to relocate to (Perform Quota
               alteration first!!))
             ALTER INITIAL MODE ('N' NO,'D' DIVISOR,'F' FACTOR,'K' KB,'M' MB)
             ALTER INITIAL QTY enter the number corresponding to above
               parameter
               ('1' NONE, appropriate number for others (digits)
             SLEEP1 Integer to specify the number of seconds to sleep
               between each index build statement to allow the RDBMS
               to cleanup the unused blocks (Otherwise it fails with
               unable to obtain initial extents, etc.)
             SLEEP2 Integer to specify the number of seconds to sleep
               between one index build and another

      This connects as system
      Grants quota unlimited on this tablespace to the owner
      Connects as the schema owner
      Generates and Executes 'ALTER INDEX  REBUILD ;'
      Generates and executes 'ALTER INDEX  REBUILD ;'
      If chosen for RULE based optimizer, then executes
       'DBMS_UTILITY.ANALYZE_SCHEMA('','DELETE');
      Connects as system
      Revokes quota unlimited on new_ts
*/

EXECUTE DBMS_OUTPUT.ENABLE (1000000);

ACCEPT UNREC PROMPT 'UNRECOVERABLE? (Know the risk?) (Y/N): '
ACCEPT OTS PROMPT 'INDEX TABLESPACE TO BE REORGANIZED: '
ACCEPT NTS PROMPT 'INDEX TABLESPACE TO USE FOR REORGANIZATION: '
ACCEPT GRTEXT PROMPT 'EXTENTS GREATER THAN (0 for all): '
ACCEPT STATDEL PROMPT 'DELETE DEFAULT STATISTICS? (Y/N): '
ACCEPT PARA PROMPT 'PARALLEL (N is for NOPARALLEL, Number for Degree): '
ACCEPT ALTIMD PROMPT 'ALTER INITIAL EXTENT MODE (N,D,F,K,M): '
ACCEPT ALTINI PROMPT 'ALTER INITIAL EXTENT (1 OR appropriate number): '
ACCEPT ALTDEST PROMPT 'ALTER DESTINATION TABLESPACE (N or TABLESPACE NAME): '
ACCEPT SLEEP1 PROMPT 'NUMBER OF SECONDS (INTEGER) TO SLEEP BETWEEN STMTS: '
ACCEPT SLEEP2 PROMPT 'NUMBER OF SECONDS (INTEGER) TO SLEEP BETWEEN TBLSP: '
ACCEPT SCHEMA PROMPT 'SCHEMA (OWNER): '
ACCEPT UPSWD PROMPT 'SCHEMA PASSWORD (not echoed): 'hide
ACCEPT SPSWD PROMPT 'SYSTEM PASSWORD (not echoed): 'hide

WHENEVER SQLERROR EXIT FAILURE ROLLBACK;
WHENEVER OSERROR EXIT FAILURE ROLLBACK;
SET VERIFY OFF
SET TIMING ON

CONN SYSTEM/&SPSWD

ALTER USER &SCHEMA QUOTA UNLIMITED ON &NTS;

CONN &SCHEMA/&UPSWD

SET SERVEROUTPUT ON SIZE 1000000

spool ${ORACLE_SID}reorg.lst

SET HEADING OFF

SELECT 'START DATE/TIME: ' || to_char(SYSDATE, 'MM/DD/YY-HH:MI:SS')
FROM DUAL;

SELECT 'USER IS: '|| user ||' -- GLOBAL DATABASE NAME IS: '|| global_name
FROM global_name;

SELECT '*** RUN PARAMETERS ARE *** UNRECOVERABLE: '||upper('&UNREC'),
       '*** INDEX TS: '||upper('&OTS'),
       '*** WORK TS: '||upper('&NTS'),
       '*** GREATER THAN EXT: '||'&GRTEXT',
       '*** SCHEMA: '||upper('&SCHEMA'),
       '*** STATISTICS DELETE: '||upper('&STATDEL'),
       '*** PARALLEL (DEGREE): '||upper('&PARA'),
       '*** INITIAL EXTENT MODE: '||upper('&ALTIMD'),
       '*** INITIAL EXTENT: '||'&ALTINI',
       '*** ALTER DESTINATION TABLESPACE: '||upper('&ALTDEST'),
       '*** SLEEP (BETWEEN ALTER INDEXES): '||'&SLEEP1',
       '*** SLEEP (BETWEEN TABLESPACES): '||'&SLEEP2'
FROM DUAL;

-- Counting the number of indexes in this tablespace for the specified owner

SELECT 'Starting Number of Indexes in this Tablespace: ', count(*)
 FROM user_segments
 WHERE segment_type = 'INDEX'
 AND tablespace_name = upper('&OTS');

-- Counting the number of indexes to reorg

SELECT 'Number of Indexes to Rebuild: ', count(*)
 FROM user_segments
 WHERE segment_type = 'INDEX'
 AND tablespace_name = upper('&OTS')
 AND extents > &GRTEXT;

SET HEADING ON

-- Beginning of pl/sql

DECLARE
  CURSOR i_r_cur1 IS
     SELECT segment_name,
            bytes/1024/1024,
            ini_trans,
            max_trans,
            a.initial_extent,
            a.next_extent,
            a.min_extents,
            a.max_extents,
            a.pct_increase,
            a.freelists,
            a.freelist_groups,
            pct_free
     FROM user_segments a,
          user_indexes b
     WHERE a.tablespace_name = upper('&OTS')
     AND a.segment_name=b.index_name
     AND a.segment_type = 'INDEX'
     AND a.extents > '&GRTEXT'
     GROUP BY segment_name,
              bytes/1024/1024,
              ini_trans,
              max_trans,
              a.initial_extent,
              a.next_extent,
              a.min_extents,
              a.max_extents,
              a.pct_increase,
              a.freelists,
              a.freelist_groups,
              pct_free
     ORDER BY 2 DESC
    ;

  CURSOR i_r_cur2 IS
     SELECT segment_name,
            bytes/1024/1024,
            ini_trans,
            max_trans,
            a.initial_extent,
            a.next_extent,
            a.min_extents,
            a.max_extents,
            a.pct_increase,
            a.freelists,
            a.freelist_groups,
            pct_free
     FROM user_segments a,
          user_indexes b
     WHERE a.tablespace_name = upper('&NTS')
     AND a.segment_name=b.index_name
     AND a.segment_type = 'INDEX'
     GROUP BY segment_name,
              bytes/1024/1024,
              ini_trans,
              max_trans,
              a.initial_extent,
              a.next_extent,
              a.min_extents,
              a.max_extents,
              a.pct_increase,
              a.freelists,
              a.freelist_groups,
              pct_free
     ORDER BY 2 DESC
    ;

  statement1      VARCHAR2(255);
  statement2      VARCHAR2(255);
  cursor_handle   INTEGER;
  indexes_rebuilt INTEGER;
  uschema         VARCHAR2(30);
  icount          NUMBER := 0;
  ocount          NUMBER := 0;
  unrecover       VARCHAR2(14) := ' UNRECOVERABLE';
  parallel        VARCHAR2(12) := NULL;
  alterini        VARCHAR2(18) := NULL;
  alterdest       VARCHAR2(30) := upper('&ALTDEST');

BEGIN
  DBMS_OUTPUT.ENABLE (1000000);

  IF upper('&PARA') != 'N'
     THEN parallel := ' PARALLEL ';
          parallel := parallel||'&PARA';
  END IF;

  IF upper('&ALTIMD') = 'K'
  OR upper('&ALTIMD') = 'M'
     THEN alterini := ' INITIAL '||'&ALTINI'||'&ALTIMD';
  END IF;

  IF upper('&ALTDEST') != 'N'
     THEN alterdest := upper('&ALTDEST');
  END IF;

  IF upper('&ALTDEST') = 'N'
     THEN alterdest := upper('&OTS');
  END IF;

  DBMS_OUTPUT.PUT_LINE('*** Beginning Loop 1');
  DBMS_OUTPUT.PUT_LINE('------------');
  FOR i_r_cur1rec IN i_r_cur1
   LOOP
    BEGIN
      icount := icount + 1;
      DBMS_OUTPUT.PUT_LINE('#');
      DBMS_OUTPUT.PUT_LINE('Loop '||icount||'/1');

    IF upper('&ALTIMD') = 'D'
     THEN alterini := ' INITIAL '||round(i_r_cur1rec.initial_extent/&ALTINI);
    END IF;

    IF upper('&ALTIMD') = 'F'
     THEN alterini := ' INITIAL '||round(i_r_cur1rec.initial_extent*&ALTINI);
    END IF;

      statement1 :='ALTER INDEX '
                  ||i_r_cur1rec.segment_name||
                  ' REBUILD TABLESPACE '
                  || '&NTS' ||
                  parallel||
                  ' PCTFREE '
                  ||i_r_cur1rec.pct_free||
                  ' INITRANS '
                  ||i_r_cur1rec.ini_trans||
                  ' MAXTRANS '
                  ||i_r_cur1rec.max_trans||
                  ' STORAGE (NEXT '
                  ||i_r_cur1rec.next_extent||
                  alterini||
                  ' MINEXTENTS '
                  ||i_r_cur1rec.min_extents||
                  ' MAXEXTENTS '
                  ||i_r_cur1rec.max_extents||
                  ' PCTINCREASE '
                  ||i_r_cur1rec.pct_increase||
                  ' FREELISTS '
                  ||i_r_cur1rec.freelists||
                  ' FREELIST GROUPS '
                  ||i_r_cur1rec.freelist_groups||
                  ' )';

    IF upper('&UNREC')= 'Y'
       THEN statement1 := statement1||unrecover;
    END IF;

    DBMS_OUTPUT.PUT_LINE('1-> '||icount||' - '||statement1);
    DBMS_OUTPUT.PUT_LINE(' ');
    cursor_handle := DBMS_SQL.OPEN_CURSOR;
    DBMS_SQL.PARSE(cursor_handle, statement1, DBMS_SQL.NATIVE);
    indexes_rebuilt := DBMS_SQL.EXECUTE(cursor_handle);
    DBMS_SQL.CLOSE_CURSOR(cursor_handle);
    DBMS_LOCK.SLEEP(&SLEEP1);

    EXCEPTION

    WHEN OTHERS
    THEN
    DBMS_SQL.CLOSE_CURSOR(cursor_handle);
    DBMS_OUTPUT.PUT_LINE('$$$$ Error Occured in - Phase 1 - '||statement1||'*');
    DBMS_OUTPUT.PUT_LINE('Error: '||to_char(SQLCODE));
    DBMS_OUTPUT.PUT_LINE(SQLERRM);
    RAISE;
    END;
  END LOOP;

  DBMS_OUTPUT.PUT_LINE('*** Ending Loop 1');
  DBMS_LOCK.SLEEP(&SLEEP2);
  DBMS_OUTPUT.PUT_LINE('*** Beginning Loop 2');
  DBMS_OUTPUT.PUT_LINE('------------');
  FOR i_r_cur2rec IN i_r_cur2
   LOOP
    BEGIN
      ocount := ocount + 1;
      DBMS_OUTPUT.PUT_LINE('#');
      DBMS_OUTPUT.PUT_LINE('Loop '||ocount||'/2');
      statement2 :='ALTER INDEX '
                  ||i_r_cur2rec.segment_name||
                  ' REBUILD TABLESPACE '
                  ||alterdest||
                  parallel||
                  ' PCTFREE '
                  ||i_r_cur2rec.pct_free||
                  ' INITRANS '
                  ||i_r_cur2rec.ini_trans||
                  ' MAXTRANS '
                  ||i_r_cur2rec.max_trans||
                  ' STORAGE (NEXT '
                  ||i_r_cur2rec.next_extent||
--                  alterini||
                  ' MINEXTENTS '
                  ||i_r_cur2rec.min_extents||
                  ' MAXEXTENTS '
                  ||i_r_cur2rec.max_extents||
                  ' PCTINCREASE '
                  ||i_r_cur2rec.pct_increase||
                  ' FREELISTS '
                  ||i_r_cur2rec.freelists||
                  ' FREELIST GROUPS '
                  ||i_r_cur2rec.freelist_groups||
                  ' )';

    IF upper('&UNREC')= 'Y'
       THEN statement2 := statement2||unrecover;
    END IF;

    DBMS_OUTPUT.PUT_LINE('2-> '||ocount||' - '||statement2);
    DBMS_OUTPUT.PUT_LINE(' ');
    cursor_handle := DBMS_SQL.OPEN_CURSOR;
    DBMS_SQL.PARSE(cursor_handle, statement2, DBMS_SQL.NATIVE);
    indexes_rebuilt := DBMS_SQL.EXECUTE(cursor_handle);
    DBMS_SQL.CLOSE_CURSOR(cursor_handle);
    DBMS_LOCK.SLEEP(&SLEEP1);

    EXCEPTION

    WHEN OTHERS
    THEN
    DBMS_SQL.CLOSE_CURSOR(cursor_handle);
    DBMS_OUTPUT.PUT_LINE('$$$$ Error Occured - Phase 2 - in '||statement2||'*');
    DBMS_OUTPUT.PUT_LINE('Error: '||to_char(SQLCODE));
    DBMS_OUTPUT.PUT_LINE(SQLERRM);
    RAISE;
    END;
    END LOOP;

    DBMS_OUTPUT.PUT_LINE('*** Ending Loop 2');

    IF upper('&STATDEL') = 'Y'
       THEN
          uschema := upper('&SCHEMA');
          DBMS_OUTPUT.PUT_LINE('*** ');
          DBMS_OUTPUT.PUT_LINE('Deleting Statistics...');
          DBMS_UTILITY.ANALYZE_SCHEMA(uschema,'DELETE');
    END IF;

END;
/

-- Counting number of indexes

SET HEAD OFF

SELECT 'Number of Indexes That Still Meets the Original Criteria: ', count(*)
 FROM user_segments
 WHERE segment_type = 'INDEX'
 AND tablespace_name = upper('&OTS')
 AND extents > &GRTEXT;

-- Counting the number of indexes in this tablespace for the specified owner

SELECT 'Ending Number of Indexes in '||'&OTS'||' Tablespace: ', count(*)
 FROM user_segments
 WHERE segment_type = 'INDEX'
 AND tablespace_name = upper('&OTS');

SELECT 'Number of Indexes in '||'&ALTDEST'||' Tablespace: ', count(*)
 FROM user_segments
 WHERE segment_type = 'INDEX'
 AND tablespace_name = upper('&ALTDEST')
 AND upper('&ALTDEST') != 'N';

SELECT 'END DATE/TIME: ' || to_char(SYSDATE, 'MM/DD/YY-HH:MI:SS')
FROM DUAL;

spool off

SET HEAD ON ECHO ON FEEDBACK ON

CONN SYSTEM/&SPSWD

ALTER TABLESPACE &OTS COALESCE;

ALTER TABLESPACE &NTS COALESCE;

ALTER USER &SCHEMA QUOTA 0K ON &NTS;

EXIT;

Have a Oracle Question
Do you have an Oracle Question?

Oracle Books
Oracle Certification, Database Administration, SQL, Application, Programming Reference Books

Oracle Application
Oracle Application Hints and Tips

Oracle Home
Oracle Database, SQL, Application, Programming Tips

All the site contents are Copyright © www.erpgreat.com and the content authors. All rights reserved.
All product names are trademarks of their respective companies.
The site www.erpgreat.com is not affiliated with or endorsed by any company listed at this site.
Every effort is made to ensure the content integrity.  Information used on this site is at your own risk.
 The content on this site may not be reproduced or redistributed without the express written permission of
www.erpgreat.com or the content authors.