Pages

Wednesday, January 29, 2014

Purging statistics from the SYSAUX tablespace

In Oracle Database, Whenever statistics in the dictionary are modified, old versions of statistics are saved automatically for future restoring purpose. But this old statistics which are saved automatically,  are purged automatically at regular intervals based on the statistics history retention setting and the time of recent statistics gathering performed in the system.

Retention is configurable using the ALTER_STATS_HISTORY_RETENTION procedure. The default value of Retention is 31 days.

If the retention period is not managed then the SYSAUX tablespaces can grow very large.

This is only a small workout in a small database with less data. So the reclaim size of the SYSAUX tablespace will be less. There will be a huge difference in space reclaim of the SYSAUX tablespace on a big database with huge data.

This blog entry will provide the scripts and steps to diagnose and correct the excessive or rapid growth of the tablespace due to retained statistics.

1. Existing Details of the SYSAUX Tablespace

set lines 200 pages 1000
column tablespace_name format a30
column allocated format 999999.99
column free format 999999.99
column used format 999999.99
col Contiguous format 999999.99
break on report
compute sum of allocated on report
compute sum of used on report
compute sum of free on report
compute sum of contiguous on report

select rpad(a.tablespace_name,30,'.')tablespace_name,
   sum(a.bytes)/(1024*1024) Allocated,
   sum(a.bytes)/(1024*1024) - max(nvl(b.space,0)) Used,
   max(nvl(b.space,0)) Free,round(((max(nvl(b.space,0)))/(sum(a.bytes)/(1024*1024))),4)*100 perctfree,
   max(nvl(b.cont,0))/(1024*1024)  Contiguous
from dba_data_files a,
   (select tablespace_name,sum(bytes)/(1024*1024) space,max(bytes) cont
    from dba_free_space
    group by tablespace_name) b
where a.tablespace_name = b.tablespace_name(+)
and a.tablespace_name='SYSAUX'
group by a.tablespace_name
order by a.tablespace_name
/



From the output we can see that only 150 MB of free space is available in SYSAUX tablespace.



COLUMN "Item" FORMAT A25
COLUMN "Space Used (GB)" FORMAT 999.99
COLUMN "Schema" FORMAT A25
COLUMN "Move Procedure" FORMAT A40

SELECT  occupant_name "Item",
    space_usage_kbytes/1048576 "Space Used (GB)",
    schema_name "Schema",
    move_procedure "Move Procedure"
FROM v$sysaux_occupants
ORDER BY 1
/



2. Check the retention of the Stats.

select dbms_stats.get_stats_history_retention from dual;


3. Alter the retention period of the Stats to 10 Days.

exec dbms_stats.alter_stats_history_retention(10);

select dbms_stats.get_stats_history_retention from dual;


4. Now execute the below command to PURGE the Old Statistics. That is purge the statistics which are older than 10 Days. 

If there is huge data, then it is recommended to purge in stages like (sysdate-30,sydate-25 etc).

exec DBMS_STATS.PURGE_STATS(SYSDATE-10);


5. Now check the History of the Statistics availability.

select dbms_stats.get_stats_history_availability from dual;


6. Show how big the tables are and rebuild after stats have been purged.

col Mb form 9,999,999
col SEGMENT_NAME form a40
col SEGMENT_TYPE form a6
set lines 120
select sum(bytes/1024/1024) Mb, segment_name,segment_type from dba_segments
where  tablespace_name = 'SYSAUX'
and segment_name like 'WRI$_OPTSTAT%'
and segment_type='TABLE'
group by segment_name,segment_type order by 1 asc
/




7. Show how big the indexes are ready for a rebuild after stats have been purged.

col Mb form 9,999,999
col SEGMENT_NAME form a40
col SEGMENT_TYPE form a6
set lines 120
select sum(bytes/1024/1024) Mb, segment_name,segment_type from dba_segments
where  tablespace_name = 'SYSAUX'
and segment_name like '%OPT%'
and segment_type='INDEX'
group by segment_name,segment_type order by 1 asc
/



8. Now we have to rebuild the table. Here in this case, enable row movement and shrink the tables are not possible since the Indexes are function based.

So use the below script to generate the move table script.

select 'alter table '||segment_name||'  move tablespace SYSAUX;' from dba_segments where tablespace_name = 'SYSAUX'
and segment_name like '%OPT%' and segment_type='TABLE'

/



9. Run the commands generated by the script.

10. Rebuild the indexes since the status of the Indexes will be in Unusable state. 



Use the below script to generate the rebuild statements.

select 'alter index '||segment_name||'  rebuild online parallel (degree 14);' from dba_segments where tablespace_name = 'SYSAUX'
and segment_name like '%OPT%' and segment_type='INDEX'

/


11. Run the rebuild statements generated by the script.

12. Before rebuilding the indexes, the status of the indexes will be in Unusable state. After rebuilding the indexes check the status of the indexes.

select  di.index_name,di.index_type,di.status  from  dba_indexes di , dba_tables dt
where  di.tablespace_name = 'SYSAUX'
and dt.table_name = di.table_name
and di.table_name like '%OPT%'
order by 1 asc
/



13. Now check the size of the SYSAUX tablespace.

set lines 200 pages 1000
column tablespace_name format a30
column allocated format 999999.99
column free format 999999.99
column used format 999999.99
col Contiguous format 999999.99
break on report
compute sum of allocated on report
compute sum of used on report
compute sum of free on report
compute sum of contiguous on report

select rpad(a.tablespace_name,30,'.')tablespace_name,
   sum(a.bytes)/(1024*1024) Allocated,
   sum(a.bytes)/(1024*1024) - max(nvl(b.space,0)) Used,
   max(nvl(b.space,0)) Free,round(((max(nvl(b.space,0)))/(sum(a.bytes)/(1024*1024))),4)*100 perctfree,
   max(nvl(b.cont,0))/(1024*1024)  Contiguous
from dba_data_files a,
   (select tablespace_name,sum(bytes)/(1024*1024) space,max(bytes) cont
    from dba_free_space
    group by tablespace_name) b
where a.tablespace_name = b.tablespace_name(+)
and a.tablespace_name='SYSAUX'
group by a.tablespace_name
order by a.tablespace_name
/



From the output we can clearly see that there is space gain of nearly 250 MB from the Sysaux tablespace.

14. Now the Retention period is set to 10 Days. 

select dbms_stats.get_stats_history_retention from dual;


If required you can bring back the retention period to the default value 31 days.

exec dbms_stats.alter_stats_history_retention(31);

select dbms_stats.get_stats_history_retention from dual;




Sunday, January 26, 2014

Query to check Allocated Space, Used Space and Free Space of the Tablesapces in Oracle Database


The below query gives the details of the Tablespaces in a Oracle Database. It shows the details of Size allocated, Used size and Free size of the tablespaces. 


NOTE: Temporary tablespace is not included in this.


set lines 200 pages 1000
column tablespace_name format a30
column allocated format 999999.99
column free format 999999.99
column used format 999999.99
col Contiguous format 999999.99
break on report
compute sum of allocated on report
compute sum of used on report
compute sum of free on report
compute sum of contiguous on report

select rpad(a.tablespace_name,30,'.')tablespace_name,
   sum(a.bytes)/(1024*1024) Allocated,
   sum(a.bytes)/(1024*1024) - max(nvl(b.space,0)) Used,
   max(nvl(b.space,0)) Free,round(((max(nvl(b.space,0)))/(sum(a.bytes)/(1024*1024))),4)*100 perctfree,
   max(nvl(b.cont,0))/(1024*1024)  Contiguous
from dba_data_files a,
   (select tablespace_name,sum(bytes)/(1024*1024) space,max(bytes) cont
    from dba_free_space
    group by tablespace_name) b
where a.tablespace_name = b.tablespace_name(+)
group by a.tablespace_name
order by a.tablespace_name
/

Example:



Friday, January 24, 2014

MIGRATE DATA FROM OLD ASM DISK TO NEW ASM DISK


1.     INTRODUCTION / OVERVIEW

Data can be migrated from one SAN disk to another in Oracle database. The below document describes the steps to migrate the data from one ASM disk to another ASM disk.

OS admin, can perform the migration if it is on the Regular File system. There is a role for a DBA to be played, when ASM comes in to picture.

The OS admin, will add the raw disks and grant the necessary permissions to the disks which are to be added. 


cd /dev
ls –lart ASM*





In this scenario the old disk are ASM_DISK1 and ASM_DISK2.

We have to add the new disks ASM_DISK3 and ASM_DISK4 to the ASM and remove the old ones.







1.     STEPS TO ADD DISK TO ASM

1.      Check for the Existing ASM Disks. Check whether the permissions of the disk are given correctly as oracle:dba.
Pluto8:/usr2/oracle />ls -lart /dev/ASM*


2.      From database level, when we query the ASM views, the disk added will be displayed in the view result. The name of the Disk Group will be specified as [CANDIDATE]. First we need to login to the ASM instance.

For Oracle 10g Version,

export ORACLE_SID=+ASM
sqlplus" / as sysdba "

 For Oracle 11g Version,

. ./.profile_grid
sqlplus /nolog
conn / as sysasm

SQL> set lines 200 pages 1000
SQL> column disk_group_name format a30
columndisk_file_path format a50
SQL> SQL> column disk_file_name format a30
SQL> column disk_file_fail_group format a30
SQL> SELECT
  2      NVL(a.name, '[CANDIDATE]')      disk_group_name
  3    , b.pathdisk_file_path
  4    , b.name                          disk_file_name
  5    , b.failgroupdisk_file_fail_group
6  FROM
  7      v$asm_diskgroup a RIGHT OUTER JOIN v$asm_disk b USING (group_number)
8  ORDER BY
9      a.name;






You can even check the disk info as below.,







3.      Add the disk to the ASM.
alterdiskgroup ASMDISK add disk '/dev/ASM_DISK3','/dev/ASM_DISK4' rebalance power 9;



4.      After adding the disk to the ASM, check the free space in ASM.



5.      After executing the command for adding disk to the ASM, the disk will be added soon but there will be a data Re-Org processing inside the ASM. So it will take some time for it to get completed. To check the status of the process issue the below query.,

select * from v$asm_operation;





From the above result we can clearly see that there is a Rebalancing operation going on in ASM. The Estimated Time for completion is 7mins.

6.      Keep on checking the status of the view v$asm_operation. At one specific time the result will selects no rows. It means the disk has been successfully added to the ASM and the Rebalance of the DATA is also completed successfully.



7.      In the same way all the disks are added to the ASM. Check the ASM view to see the status of the Added disks.,



2.     REMOVE DISK FROM ASM

Deletion / Removal of disks are similar to the Addition of disks to the ASM. The command to remove the disk will vary.

In Migration of ASM Disk, to add a disk to the ASM we use the OS Level name of the disk, whereas while dropping / removing a disk from ASM we should specify the Database level name of the Disk. This we can get it by querying the view v$asm_diskgroup.

The below are the steps to carry out the Removal Of Disk from ASM.

1.    Check the current status of the ASM.





The above is the current status of the ASM.

2.      To drop or remove a disk from ASM issue the below command.,
alterdiskgroup ASMDISK drop disk ASMDISK_0000,ASMDISK_0001 rebalance power 9;



3.      As we saw in the addition of disk, here also a Disk Re-Org or Rebalance operation will be processed.



4.      Wait till the Rebalance operation to complete. The view should return no rows.



5.      Now check the status of ASM disks,








1.      Once after removing the disk from the ASM, a restart of the database is required. There will be some locks existing on disks in the OS level. So OS team will be not able to remove the old disks.

2.      After restarting the Database inform to OS team so that they will easily remove the old Disks.

1.     CONCLUSION

a.      The Migration of Data can be done Online.
b.      Only removal of disk needs a reboot whereas for adding disks restart of the database is not required.






Friday, January 17, 2014

PREVENT USERS (SCHEMAS) FROM GETTING DROPPED


The below is the Trigger which is used to prevent the users or schemas from getting dropped.

Create OR Replace Trigger TrgDropUserRestrict
Before Drop On Database
Declare
Begin
     If Ora_Dict_Obj_Name In ('SH','OUTLN','SCOTT','user1')    Then
                     Raise_Application_Error(-20001,'Cannot Drop User
                    '||ora_dict_obj_name||' Contact Your Database Administrator For Dropping This User !');
     End If;
End;
/


1. create a new user and grant privileges to the new user.

ORADB>create user user1 identified by laser default tablespace users;

User created.

ORADB>grant connect,resource,debug connect session,debug any procedure to user1;

Grant succeeded.

ORADB>    

ORADB>select username from all_users where username = 'USER1';

USERNAME
------------------------------
USER1

ORADB>

2. Run the procedure to prevent the user to be dropped.

ORADB>Create OR Replace Trigger TrgDropUserRestrict
Before Drop On Database
Declare
Begin
     If Ora_Dict_Obj_Name In ('SH','OUTLN','SCOTT','USER1')    Then
                     Raise_Application_Error(-20001,'Cannot Drop User
                    '||ora_dict_obj_name||' Contact Your Database Administrator For Dropping This User !'); 
     End If;
End;
/  2    3    4    5    6    7    8    9   10  

Trigger created.



Note: You can specify N number of users in the Ora_Dict_Obj_Name which should not get dropped.



3. Try to drop the user and check if the user is dropped.

ORADB>drop user user1 cascade;
drop user user1 cascade
*
ERROR at line 1:
ORA-00604: error occurred at recursive SQL level 1
ORA-20001: Cannot Drop User
user1 Contact Your Database Administrator For Dropping This User !
ORA-06512: at line 4

ORADB>

ORADB>drop user scott cascade;
drop user scott cascade
*
ERROR at line 1:
ORA-00604: error occurred at recursive SQL level 1
ORA-20001: Cannot Drop User
SCOTT Contact Your Database Administrator For Dropping This User !
ORA-06512: at line 4


Thursday, January 9, 2014

Unable to remove ASM Diskgroup from the ASM Instance

SCENARIO


  • I Was not able to remove the newly added ASM diskgroup ASM_DISK1 from ASM instance.


INSTANCE - ASM

ASM GRID - ORACLE 11G

REMOVED DISKGROUP NAME - ASM_DISK1

OS LEVEL DISK NAME ASSIGNED - DATA1

OPERATING SYSTEM - OEL 6.0


WORKINGS


SQL> select name from v$asm_diskgroup;

NAME
---------------------------------
DATA_GRP1
ASM_DISK1

SQL> select name from v$asm_disk;

NAME
---------------------------------
DATA_GRP1_0000
ASM_DISK1_0000

  • While dropping the diskgroup, i got the following error and I was not able to drop it.,

SQL> alter diskgroup ASM_DISK1 drop disk ASM_DISK1_0000;
alter diskgroup ASM_DISK1 drop disk ASM_DISK1_0000
*
ERROR at line 1:
ORA-15032: not all alterations performed
ORA-15250: insufficient diskgroup space for rebalance completion


SQL> alter diskgroup ASM_DISK1 drop disk ASM_DISK1_0000 rebalance power 8;
alter diskgroup ASM_DISK1 drop disk ASM_DISK1_0000 rebalance power 8
*
ERROR at line 1:
ORA-15032: not all alterations performed
ORA-15250: insufficient diskgroup space for rebalance completion

  • Checked the Total space and Free space of the ASM Diskgroup. Since the disk was a newly added one, there was enough free space.

SQL> set lines 200 pages 1000
SQL> col name format a30
SQL> select name,total_mb,free_mb from v$asm_disk;

NAME TOTAL_MB    FREE_MB
------------------------------ ----------         ----------
DATA_GRP1_0000   21050           20998
ASM_DISK1_0000   5567        5508


  • I tried to drop it again,


SQL> drop diskgroup ASM_DISK1 including contents;
drop diskgroup ASM_DISK1 including contents
*
ERROR at line 1:
ORA-15039: diskgroup not dropped
ORA-15027: active use of diskgroup "ASM_DISK1" precludes its dismount

  • I dismounted the diskgroup and tried to drop it.

SQL> alter diskgroup ASM_DISK1 dismount force;

Diskgroup altered.

SQL> drop diskgroup ASM_DISK1 including contents;
drop diskgroup ASM_DISK1 including contents
*
ERROR at line 1:
ORA-15039: diskgroup not dropped
ORA-15001: diskgroup "ASM_DISK1" does not exist or is not mounted

  • The below was the solution which I followed to remove the ASM diskgroup from the ASM Instance.

  • Created a pfile from the spfile in a different location and restarted the database using that pfile.

SQL> show parameter spfile 


NAME  TYPE      VALUE
------------------------ -------------------  ------------------------------------------------------------------------------
spfile  string      +ASM_DISK1/asm/asmparameterfile/registry.253.836335963


SQL> create pfile='/vol2/grid/init.ora' from spfile;

File created.

SQL> shut immediate
ASM diskgroups dismounted
ASM instance shutdown
SQL> startup nomount pfile='/vol2/grid/init.ora';
ASM instance started

Total System Global Area  283930624 bytes
Fixed Size    2227664 bytes
Variable Size  256537136 bytes
ASM Cache   25165824 bytes
SQL> 

SQL> alter diskgroup ASM_DISK1 mount;

Diskgroup altered.

SQL> drop diskgroup ASM_DISK1 including contents;

Diskgroup dropped.


  • Now mount the other ASM diskgroup.

SQL> alter diskgroup DATA_GRP1 mount;

Diskgroup altered.

SQL> create spfile from pfile='/vol2/grid/init.ora';

File created.

SQL> shut immediate
ASM diskgroups dismounted
ASM instance shutdown


  • Now create a spfile from the pfile and start up the instance.

SQL> create spfile from pfile='/vol2/grid/init.ora';

File created.
SQL> shut immediate
ASM diskgroups dismounted
ASM instance shutdown
SQL> 
SQL> startup
ASM instance started

Total System Global Area  283930624 bytes
Fixed Size    2227664 bytes
Variable Size  256537136 bytes
ASM Cache   25165824 bytes
ORA-15032: not all alterations performed
ORA-15017: diskgroup "ASM_DISK1" cannot be mounted
ORA-15063: ASM discovered an insufficient number of disks for diskgroup
"ASM_DISK1"


  • Here I faced another error. Even though after successful dropping of the ASM Disk, while starting the ASM instance, it automatically referred the removed disk also.
  • So I deleted the ASM disk using the Oracleasm command,

oracleasm deletedisk DATA1

[root@primary ~]# oracleasm deletedisk DATA1
Clearing disk header: done
Dropping disk: done
[root@primary ~]# oracleasm listdisks
DATA2
[root@primary ~]# oracleasm scandisks
Reloading disk partitions: done
Cleaning any stale ASM disks...
Scanning system for ASM disks...
[root@primary ~]# 


  • I also changed the value of ORACLEASM_SCANORDER="" to ORACLEASM_SCANORDER="dm". But this also did not work.

primary:/vol2/grid />cat /etc/sysconfig/oracleasm
#
# This is a configuration file for automatic loading of the Oracle
# Automatic Storage Management library kernel driver.  It is generated
# By running /etc/init.d/oracleasm configure.  Please use that method
# to modify this file
#

# ORACLEASM_ENABELED: 'true' means to load the driver on boot.
ORACLEASM_ENABLED=true

# ORACLEASM_UID: Default user owning the /dev/oracleasm mount point.
ORACLEASM_UID=

# ORACLEASM_GID: Default group owning the /dev/oracleasm mount point.
ORACLEASM_GID=

# ORACLEASM_SCANBOOT: 'true' means scan for ASM disks on boot.
ORACLEASM_SCANBOOT=true

# ORACLEASM_SCANORDER: Matching patterns to order disk scanning
ORACLEASM_SCANORDER=dm

# ORACLEASM_SCANEXCLUDE: Matching patterns to exclude disks from scan
ORACLEASM_SCANEXCLUDE=""

primary:/vol2/grid />


  • Even though after deleting the ASM disk in OS level, the ASM instance was starting with the error.

SQL> conn / as sysasm
Connected to an idle instance.
SQL> startup
ASM instance started

Total System Global Area  283930624 bytes
Fixed Size    2227664 bytes
Variable Size  256537136 bytes
ASM Cache   25165824 bytes
ORA-15032: not all alterations performed
ORA-15017: diskgroup "ASM_DISK1" cannot be mounted
ORA-15063: ASM discovered an insufficient number of disks for diskgroup
"ASM_DISK1"


SOLUTION

  • Remove the old diskgroup name from the parameter file (PFILE) asm_diskgroups.

SQL> show parameter asm_diskgroups

NAME   TYPE  VALUE
------------------------------  ----------------  ------------------------------
asm_diskgroups   string   DATA_GRP1, ASM_DISK1
SQL> 

SQL> alter system set asm_diskgroups='DATA_GRP1' scope=both;

System altered.

SQL> show parameter disk

NAME   TYPE  VALUE
------------------------------  ----------------  ------------------------------
asm_diskgroups   string  DATA_GRP1
asm_diskstring   string /dev/oracleasm/disks/*
SQL>

  • Create a pfile from spfile.

SQL> create pfile='/vol2/grid/init.ora' from spfile;

File created.

  • Startup the ASM instance with the pfile.

SQL> shut immediate
ASM diskgroups dismounted
ASM instance shutdown

SQL> startup pfile='/vol2/grid/init.ora';
ASM instance started

Total System Global Area  283930624 bytes
Fixed Size     2227664 bytes
Variable Size   256537136 bytes
ASM Cache    25165824 bytes
ASM diskgroups mounted
SQL>

  • ASM Started normally.

  • Create a SPFILE and restart the ASM instance.

SQL> create spfile from pfile='/vol2/grid/init.ora';

File created.

SQL> shut immediate;
ASM diskgroups dismounted
ASM instance shutdown
SQL> startup
ASM instance started

Total System Global Area  283930624 bytes
Fixed Size    2227664 bytes
Variable Size  256537136 bytes
ASM Cache   25165824 bytes
ASM diskgroups mounted
SQL> 

SQL> set lines 200 pages 1000
SQL> col name format a25
SQL> select name,total_mb,free_mb from v$asm_disk;

NAME    TOTAL_MB FREE_MB
-------------------------   ----------      ----------
DATA_GRP1_0000     21050         20998

SQL> select name,total_mb,free_mb from v$asm_diskgroup;

NAME     TOTAL_MB FREE_MB
-------------------------   ----------      ----------
DATA_GRP1          21050         20998

SQL> 

  • Now we can see that there was no error message during the instance startup.



Wednesday, January 1, 2014

Table De-Fragmentation to Reclaim the Space and Different Methods of De-Fragmentation

ENVIRONMENT
=============


SERVER NAME

SERVER1

DATABASE NAME

TESTDB

SCHEMA NAME

VSRIDHAR

FRAGMENTED TABLE

MYTABLE


There are many ways to reclaim the space from the table. Some of the Traditional ways are below.,

1.     Imp/exp
2.     Alter tablespace move
3.     Truncate and Insert
4.     user dbms_redefinition package to copy the table

Oracle New Feature is

1.     Using shrink feature of Oracle 10g
a.      Shrink compact
b.     Shrink cascade

2.     Redefinition of Tables using DBMS_REDIFINITION

A Brief about SHRINK tables
=======================

SHRINK CLAUSE

             Alter table table_name shrink space;

      The shrink clause lets you manually shrink space in a table, index-organized table or its overflow segment, index, partition, sub partition, LOB segment, materialized view, or materialized view log. This clause is valid only for segments in tablespaces with automatic segment management. By default, Oracle Database compacts the segment, adjusts the high water mark, and releases the recuperated space immediately.

      Compacting the segment requires row movement. Therefore, you must enable row movement for the object you want to shrink before specifying this clause. Further, if your application has any rowid-based triggers, you should disable them before issuing this clause.

Note:

       Do not attempt to enable row movement for an index-organized table before specifying the shrink_clause. The ROWID of an index-organized table is its primary key, which never changes. Therefore, row movement is neither relevant nor valid for such tables.




COMPACT

Alter table table_name shrink space compact;

        If you specify COMPACT, then Oracle Database only defragments the segment space and compacts the table rows for subsequent release. The database does not readjust the high water mark and does not release the space immediately. You must issue another ALTER TABLE ... SHRINK SPACE statement later to complete the operation. This clause is useful if you want to accomplish the shrink operation in two shorter steps rather than one longer step.

        Alter table table_name shrink space compact;

        Alter table table_name shrink space;   ----Row level locking happen. This                                                                                                     command can be executed in OFF                                                                                                 Peak Hours.                                                                                                                                
The COMPACT clause lets you divide the shrink segment operation into two phases. When you specify COMPACT, Oracle Database defragments the segment space and compacts the table rows but postpones the resetting of the high water mark and the deallocation of the space until a future time. This option is useful if you have long-running queries that might span the operation and attempt to read from blocks that have been reclaimed. The defragmentation and compaction results are saved to disk, so the data movement does not have to be redone during the second phase. You can reissue the SHRINK SPACE clause without the COMPACT clause during off-peak hours to complete the second phase.



CASCADE

           alter table table_name shrink space cascade;

The CASCADE clause extends the segment shrink operation to all dependent segments of the object. For example, if you specify CASCADE when shrinking a table segment, all indexes of the table will also be shrunk. (You need not specify CASCADE to shrink the partitions of a partitioned table.) To see a list of dependent segments of a given object, you can run the OBJECT_DEPENDENT_SEGMENTS procedure of the DBMS_SPACE package

To shrink a basicfile LOB:

alter table <table_name> MODIFY LOB <column_name> shrink space

To shrink a single partition of a partition table:

alter table <table_name> MODIFY PARTITION <partition_name> shrink                   space;

Oracle split this process as below.,

       Oracle move the rows which are located in the middle or at the end of a segment further more down to the beginning of the segment and make the segment more compact. This process is moving the data row by row. It acquires a row level lock when the row is moved down to the beginning of the segment. The corresponding index data will be handled like any other row level DML. So we do not need to worry about rebuilding the indexes for the row. Also row level lock will happen for very short moment. Before we start this phase, we need to enable row movement.

Here we have used the 1st Method - Shrink feature to reclaim the space from the table.

1-a. Shrink compact feature

Firstly, Created a table "MYTABLE" using the below Procedure which also populates the table with 10000000 records. The steps

SET ECHO ON
SET SERVEROUTPUT ON SIZE 1000000

CREATE TABLE MYTABLE
  (id                NUMBER              NOT NULL
  ,CONSTRAINT id_pk  PRIMARY KEY (id));
DECLARE
       TYPE number_table IS TABLE OF MYTABLE.id%TYPE INDEX BY BINARY_INTEGER;
       number_list NUMBER_TABLE;
  BEGIN
   FOR i IN 1..10000000 LOOP
          number_list(i) := i;
    END LOOP;
    FORALL i IN 1..number_list.COUNT
      INSERT INTO MYTABLE VALUES (number_list(i));
    COMMIT;
  END;
  /

**********************************************************************

Created the table "MYTABLE”
=======================

CREATE TABLE mytable
  (id                NUMBER              NOT NULL
  ,CONSTRAINT id_pk  PRIMARY KEY (id));
SQL> SQL>   2    3
Table created.
SQL>
SQL> DECLARE
  2
  3      TYPE number_table IS TABLE OF mytable.id%TYPE INDEX BY BINARY_INTEGER;
  4    5
  6      number_list NUMBER_TABLE;
  7
  8    BEGIN
  9
 10
 11      FOR i IN 1..10000000 LOOP
 12
 13        number_list(i) := i;
 14
 15      END LOOP;
 16
 17
 18      FORALL i IN 1..number_list.COUNT
 19        INSERT INTO mytable VALUES (number_list(i));
 20
 21      COMMIT;
 22
 23    END;
 24    /

PL/SQL procedure successfully completed.

Populated the table with 10000000 records
==================================

SQL> select count(*) from vsridhar.mytable;

COUNT(*)
--------------
10000000


Size and Blocks of the table "MYTABLE"
==============================

SQL> select SEGMENT_NAME,SEGMENT_TYPE,BYTES/1024/1024 "SIZE_MB",MAX_SIZE/1024/1024 "MAX_SIZE_MB" from dba_segments where SEGMENT_NAME='MYTABLE';

SEGMENT_NAME         SEGMENT_TYPE                                              SIZE_MB     MAX_SIZE_MB
--------------------       ------------------------------------------------------ ----------        -----------
MYTABLE              TABLE                                                                          120               2048

SQL> select blocks from dba_tables where table_name='MYTABLE';

BLOCKS
----------
15197

**********************************************************************

Fragmentation Method 1-a:
======================

Step 1:
======

Analyze the Table.,

analyze table vsridhar.MYTABLE compute statistics;

SQL> analyze table vsridhar.MYTABLE compute statistics;
Table analyzed.

Step 2:
=====

Check the Size and Blocks of the table.,

select SEGMENT_NAME,SEGMENT_TYPE,BYTES/1024/1024 "SIZE_MB",MAX_SIZE/1024/1024 "MAX_SIZE_MB" from dba_segments where SEGMENT_NAME='MYTABLE';

select blocks from dba_tables where table_name='MYTABLE';

SQL> select blocks from dba_tables where table_name='MYTABLE';
 BLOCKS
----------
15197

SQL> select SEGMENT_NAME,SEGMENT_TYPE,BYTES/1024/1024 "SIZE_MB",MAX_SIZE/1024/1024 "MAX_SIZE_MB" from dba_segments where SEGMENT_NAME='MYTABLE';

SEGMENT_NAME         SEGMENT_TYPE           SIZE_MB    MAX_SIZE_MB
--------------------          ---------------------------     ------------  -------------------
   MYTABLE                  TABLE                             120           2048


Step 3:
=====

Delete some rows from table and analyze the table again.,

analyze table vsridhar.MYTABLE compute statistics;

SQL> delete from vsridhar.mytable where id like '%9%';
5217031 rows deleted.

SQL> select count(*) from vsridhar.mytable;

COUNT(*)
----------
4782969

SQL> analyze table vsridhar.MYTABLE compute statistics;
Table analyzed.

Step 4:
=====

Again check the blocks and size of the table whether the blocks are reduced or not.,

select SEGMENT_NAME,SEGMENT_TYPE,BYTES/1024/1024 "SIZE_MB",MAX_SIZE/1024/1024 "MAX_SIZE_MB" from dba_segments where SEGMENT_NAME='MYTABLE';

select blocks from dba_tables where table_name='MYTABLE';

SQL> select SEGMENT_NAME,SEGMENT_TYPE,BYTES/1024/1024 "SIZE_MB",MAX_SIZE/1024/1024 "MAX_SIZE_MB" from dba_segments where SEGMENT_NAME='MYTABLE';

SEGMENT_NAME       SEGMENT_TYPE       SIZE_MB      MAX_SIZE_MB
------------------------    -------------------------  --------------- --------------------
   MYTABLE                TABLE                        120              2048


SQL> select blocks from dba_tables where table_name='MYTABLE';

BLOCKS
----------
15197


Step 5:
=====

Before shrinking the table, one should enable the row movement in that table. After enabling the row movement, Shrinking contains of two simple steps.,

alter table vsridhar.MYTABLE enable row movement;

alter table vsridhar.MYTABLE shrink space compact;


SQL> alter table vsridhar.MYTABLE shrink space compact;
Table altered.

Step 6:
======

Now just for checking purpose, check the size and blocks of the table.,

select SEGMENT_NAME,SEGMENT_TYPE,BYTES/1024/1024 "SIZE_MB",MAX_SIZE/1024/1024 "MAX_SIZE_MB" from dba_segments where SEGMENT_NAME='MYTABLE';

select blocks from dba_tables where table_name='MYTABLE';

SQL> analyze table vsridhar.MYTABLE compute statistics;
Table analyzed.

SQL> select SEGMENT_NAME,SEGMENT_TYPE,BYTES/1024/1024 "SIZE_MB",MAX_SIZE/1024/1024 "MAX_SIZE_MB" from dba_segments where SEGMENT_NAME='MYTABLE';

SEGMENT_NAME         SEGMENT_TYPE                                              SIZE_MB MAX_SIZE_MB
  --------------------          -------------------------------------------------- ------------   -------------------
   MYTABLE              TABLE                                                                          120          2048

SQL> select blocks from dba_tables where table_name='MYTABLE';

BLOCKS
----------
15197

Step 7:
======

Now Execute the next statement to get the accurate result i.e., to shrink the table completely.

alter table vsridhar.MYTABLE shrink space;

analyze table vsridhar.MYTABLE compute statistics;

SQL> alter table vsridhar.MYTABLE shrink space;
Table altered.

SQL> analyze table vsridhar.MYTABLE compute statistics;
Table analyzed.


Step 8:
=====

Now check the blocks and size using the below query and we can clearly see the difference in space.,

select SEGMENT_NAME,SEGMENT_TYPE,BYTES/1024/1024 "SIZE_MB",MAX_SIZE/1024/1024 "MAX_SIZE_MB" from dba_segments where SEGMENT_NAME='MYTABLE';

select blocks from dba_tables where table_name='MYTABLE';

SQL> select SEGMENT_NAME,SEGMENT_TYPE,BYTES/1024/1024 "SIZE_MB",MAX_SIZE/1024/1024 "MAX_SIZE_MB" from dba_segments where SEGMENT_NAME='MYTABLE';

SEGMENT_NAME         SEGMENT_TYPE                  SIZE_MB      MAX_SIZE_MB
--------------------            -------------------------------    ---------------  -------------------
MYTABLE                       TABLE                                 59.8125        2048

SQL> select blocks from dba_tables where table_name='MYTABLE';
BLOCKS
----------
7521

Note:
=====
When shrinking was in process, simultaneously we have also tested some DML operations on the same table. It worked fine. So this process can be carried out ONLINE.

SQL> select count(*) from mytable where id like '%1%';

COUNT(*)
----------
2685818

SQL> update mytable set id=9 where id=1;

1 row updated.

SQL> commit;

Commit complete.

SQL> select * from mytable where id=9;
  ID
----------
   9

SQL> select * from mytable where id=1;

no rows selected

**********************************************************************


Fragmentation Method 1-b – Using “shrink cascade” command

1.     Checked the Size of the segments.,

SQL> select segment_name,bytes/1024/1024 size_mb from dba_segments where owner='VSRIDHAR';

SEGMENT_NAME            SIZE_MB
--------------------                  ----------
MYTABLE                           58
ID_PK2                                 88

2.     Checked the count of the table “MYTABLE”

SQL> select count(*) from vsridhar.mytable;

COUNT(*)
----------
4782969

3.     Deleted some rows from the table “MYTABLE”.

SQL> delete from vsridhar.mytable where id like '%2%';

2685817 rows deleted.

4.     Checked the size again after deleting but the size of the segment is not reduced.,

SQL> select segment_name,bytes/1024/1024 size_mb from dba_segments where owner='VSRIDHAR';

SEGMENT_NAME            SIZE_MB
--------------------                  ----------
MYTABLE                           58
ID_PK2                                 88

5.     Checked the status of the index and analyzed the table.,

SQL> select index_name,table_name,owner,status from dba_indexes where table_name='MYTABLE';

INDEX_NAME           TABLE_NAME           OWNER                STATUS
--------------------       --------------------        --------------------   -------------------
ID_PK2                        MYTABLE               VSRIDHAR             VALID

SQL> analyze table vsridhar.mytable compute statistics;

Table analyzed.

SQL> select segment_name,bytes/1024/1024 size_mb from dba_segments where owner='VSRIDHAR';

SEGMENT_NAME            SIZE_MB
--------------------                  ----------
MYTABLE                           58
ID_PK2                                 88

Even after analyzing the table the size of the table did not reduce.

STEP 1:
======

Enable the row movement for that table by using the below command.,

alter table vsridhar.mytable enable row movement;

SQL> alter table vsridhar.mytable enable row movement;

Table altered.

STEP 2:
======

Shrink the table using the below command to reclaim the space.,

alter table vsridhar.mytable shrink space cascade;


SQL> alter table vsridhar.mytable shrink space cascade;

Table altered.

STEP 4:
======

After shrinking the table check the size of the table and also check whether the index to the corresponding table is in the VALID state or not.

select segment_name,bytes/1024/1024 size_mb from dba_segments where owner='VSRIDHAR';

select index_name,table_name,owner,status from dba_indexes where table_name='MYTABLE';


SQL> select segment_name,bytes/1024/1024 size_mb from dba_segments where owner='VSRIDHAR';

SEGMENT_NAME            SIZE_MB
--------------------                  ----------
MYTABLE                           25.4375
ID_PK2                                 37.125

SQL> select index_name,table_name,owner,status from dba_indexes where table_name='MYTABLE';

INDEX_NAME           TABLE_NAME           OWNER                STATUS
--------------------      --------------------       --------------------   --------------------
ID_PK2                     MYTABLE                 VSRIDHAR                VALID

********************************************************************** 
Fragmentation Method 2:
====================


ONLINE TABLE REDEFINITION

The Oracle online table reorganization package, (dbms_redefinition) is used to reorganize tables while they are accepting updates.  See here for details on using the dbms_redefinition package for reorganizing Oracle tables online.  The online reorganization packages does this by creating a snapshot on the target table and applying all table changes after the table has been reorganized with the "Create table as select" command:


Before going to the steps, as we did in the earlier stage create a table "MYTABLE".

SQL> SET ECHO ON
SQL> SET SERVEROUTPUT ON SIZE 1000000

CREATE TABLE mytable
  (id                NUMBER              NOT NULL
  ,CONSTRAINT id_pk  PRIMARY KEY (id));
SQL> SQL>   2    3
DECLARE

    TYPE number_table IS TABLE OF mytable.id%TYPE INDEX BY BINARY_INTEGER;

    number_list NUMBER_TABLE;

  BEGIN
    FOR i IN 1..10000000 LOOP
      number_list(i) := i;
    END LOOP;
    FORALL i IN 1..number_list.COUNT
     INSERT INTO mytable VALUES (number_list(i));
    COMMIT;
  END;

Table created.

SQL> SQL>   2    3    4    5    6    7    8    9   10   11   12   13   14   15   16   17   18   19   20   21   22   23   24    /

PL/SQL procedure successfully completed.

SQL> select count(*) from vsridhar.mytable;

  COUNT(*)
  ----------
  10000000

SQL> analyze table vsridhar.mytable compute statistics;

Table analyzed.

Elapsed: 00:01:08.66

SQL> select SEGMENT_NAME,SEGMENT_TYPE,BYTES/1024/1024 "SIZE_MB",MAX_SIZE/1024/1024 "MAX_SIZE_MB" from dba_segments where SEGMENT_NAME='MYTABLE';

SEGMENT_NAME         SEGMENT_TYPE                                              SIZE_MB MAX_SIZE_MB
-------------------- ------------------------------------------------------ ---------- -----------
MYTABLE              TABLE                                                         120        2048

Elapsed: 00:00:00.18

SQL> select blocks from dba_tables where table_name='MYTABLE';

    BLOCKS
    ----------
     15197

Elapsed: 00:00:00.03


Delete some rows from the table "MYTABLE":
=====================================

delete from vsridhar.mytable where id like '%9%'
/

SQL> delete from vsridhar.mytable where id like '%9%'
/  2

5217031 rows deleted.

Elapsed: 00:03:06.87

SQL> select count(*) from vsridhar.mytable;

  COUNT(*)
   ----------
   4782969

Elapsed: 00:00:00.23
SQL>


Analyze the Table again and check the size and blocks of the Table:
=====================================================

analyze table vsridhar.MYTABLE compute statistics;

select SEGMENT_NAME,SEGMENT_TYPE,BYTES/1024/1024 "SIZE_MB",MAX_SIZE/1024/1024 "MAX_SIZE_MB" from dba_segments where SEGMENT_NAME='MYTABLE';

select blocks from dba_tables where table_name='MYTABLE';


SQL> analyze table vsridhar.MYTABLE compute statistics;

Table analyzed.

Elapsed: 00:00:37.25

SQL> select SEGMENT_NAME,SEGMENT_TYPE,BYTES/1024/1024 "SIZE_MB",MAX_SIZE/1024/1024 "MAX_SIZE_MB" from dba_segments where SEGMENT_NAME='MYTABLE';

SEGMENT_NAME         SEGMENT_TYPE                                              SIZE_MB MAX_SIZE_MB
-------------------- ------------------------------------------------------ ---------- -----------
MYTABLE              TABLE                                                         120        2048

Elapsed: 00:00:00.23

SQL> select blocks from dba_tables where table_name='MYTABLE';

    BLOCKS
    ----------
     15197

Elapsed: 00:00:00.02

From the above we can clearly see there was no difference in the size and blocks of the table even after the deletion of rows. The space can be reclaimed using the ONLINE TABLE REDEFINITION Feature in Oracle.


Please follow the below steps for Online Table Redefinition., - Method 2


STEP 1:
======

Check whether the table can be redefined or not.

EXEC Dbms_Redefinition.Can_Redef_Table('VSRIDHAR', 'MYTABLE');

SQL> EXEC Dbms_Redefinition.Can_Redef_Table('VSRIDHAR', 'MYTABLE');

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.53



STEP 2:
======

Create new table with CTAS - "Creating Table As Select"

CREATE TABLE VSRIDHAR.MYTABLE2
TABLESPACE mis AS
SELECT * FROM VSRIDHAR.MYTABLE
/

SQL> CREATE TABLE VSRIDHAR.MYTABLE2
TABLESPACE mis AS
SELECT * FROM VSRIDHAR.MYTABLE
/  2    3    4

Table created.

Elapsed: 00:00:02.69


STEP 3:
=======

Start Redefinition

EXEC Dbms_Redefinition.Start_Redef_Table( -
  'VSRIDHAR', -
  'MYTABLE', -
  'MYTABLE2', -
  'ID ID');

SQL> EXEC Dbms_Redefinition.Start_Redef_Table( -
  'VSRIDHAR', -
  'MYTABLE', -
  'MYTABLE2', -
  'ID ID');> > > >

PL/SQL procedure successfully completed.

Elapsed: 00:00:04.32


STEP 4:
======

Optionally synchronize new table with interim data

EXEC dbms_redefinition.sync_interim_table( -
  'VSRIDHAR', 'MYTABLE', 'MYTABLE2');

SQL> EXEC dbms_redefinition.sync_interim_table( -
  'VSRIDHAR', 'MYTABLE', 'MYTABLE2');>

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.06

 STEP 5:
=======

Add new keys, FKs and triggers

ALTER TABLE VSRIDHAR.MYTABLE2 ADD (CONSTRAINT id_pk2 PRIMARY KEY (id))
/

SQL> ALTER TABLE VSRIDHAR.MYTABLE2 ADD (CONSTRAINT id_pk2 PRIMARY KEY (id))
/  2

Table altered.

Elapsed: 00:00:08.35


 STEP 6:
=======

Complete redefinition

EXEC Dbms_Redefinition.Finish_Redef_Table( -
  'VSRIDHAR', 'MYTABLE', 'MYTABLE2');


SQL> EXEC Dbms_Redefinition.Finish_Redef_Table( -
  'VSRIDHAR', 'MYTABLE', 'MYTABLE2');>

PL/SQL procedure successfully completed.

Elapsed: 00:00:02.07


STEP 7:
=======

Remove original table which now has the name of the new table

DROP TABLE VSRIDHAR.MYTABLE2;

SQL> DROP TABLE VSRIDHAR.MYTABLE2;

Table dropped.

Elapsed: 00:00:00.49


AFTER TABLE ONLINE REDEFINITION SIZE AND BLOCKS OF THE TABLE:
========================================================

SQL> select SEGMENT_NAME,SEGMENT_TYPE,BYTES/1024/1024 "SIZE_MB",MAX_SIZE/1024/1024 "MAX_SIZE_MB" from dba_segments where SEGMENT_NAME='MYTABLE';

SEGMENT_NAME         SEGMENT_TYPE    SIZE_MB     MAX_SIZE_MB
--------------------            ----------------------  -------------  -------------------
MYTABLE              TABLE                            58               2048

Elapsed: 00:00:00.66


SQL> select blocks from dba_tables where table_name='MYTABLE';

    BLOCKS
   ----------


Elapsed: 00:00:00.23

Note: Since the table is not analyzed the result shows the blocks as empty.

SQL> analyze table vsridhar.mytable compute statistics;

Table analyzed.

Elapsed: 00:00:34.30


SQL> select blocks from dba_tables where table_name='MYTABLE';

    BLOCKS
    ----------
      7405

Elapsed: 00:00:00.01

select status,constraint_name from DBA_constraints where table_name = 'MYTABLE';


SQL> select status,constraint_name from DBA_constraints where table_name = 'MYTABLE';

STATUS                   CONSTRAINT_NAME
------------------------ ------------------------------------------------------------------------------------------
ENABLED                  SYS_C009785
ENABLED                  ID_PK2

Elapsed: 00:00:00.26


There are several advantages over traditional methods.  The advantages are below.,

1.     It can be done in online. There is a table level lock for very short moment. Traditional methods are not supporting to reset the HWM in online.

2.     It does not take extra space while resetting the HWM. If we use traditional method, DBMS_REDEFINITION package uses double the amount of space.

3.      It does acquire only row level lock while performing majority of the shrinking (moving rows) work. It acquires table level lock only when it resets the HWM which is in phase II. But traditional methods requires table down time for resetting the HWM except using dbms_redefinition package.

4.      Index will be maintained and remain usable. But in traditional methods, we need to rebuild the index. Especially when we use ALTER TABLESPACE MOVE command.

5.     It can be made in one command (alter table EMP shrink space). In traditional method, there are multiple steps.

6.     If you are not sure that you can afford table level lock at specific time, then you can do the majority of the shrinking work and later we can reset the HWM. Since table level lock is required only while resetting the HWM. The whole process can be done in two steps. This advantage is not available in traditional methods.

Some of the Restrictions are.,

1.     It is only possible in ASSM tablespace

2.     Not supporting for clustered tables, tables with column data type           LONG