When a lot of DML operations happens on a table, the table will become fragmented because DML does not release free space from the table below the HWM. So despite having less number of rows, due to fragmentation, it consumes more space. So it is best practice to re-org the oracle table regularly.

In this below example, we will consider the table DBATEST.TEST1 :

SQL> select count(*) from DBATEST.TEST1;

  COUNT(*)
----------
   1450176
   
   
SQL> SELECT SUM(BYTES/1024/1024) FROM DBA_SEGMENTS WHERE OWNER='DBATEST' and segment_name='TEST1';

SUM(BYTES/1024/1024)
--------------------
                 168

Below script to get the current fragmentation details on the table:

set serveroutput on
declare
v_unformatted_blocks number;
v_unformatted_bytes number;
v_fs1_blocks number;
v_fs1_bytes number;
v_fs2_blocks number;
v_fs2_bytes number;
v_fs3_blocks number;
v_fs3_bytes number;
v_fs4_blocks number;
v_fs4_bytes number;
v_full_blocks number;
v_full_bytes number;
begin
dbms_space.space_usage ('&SCHEMA_NAME', '&TABLE_NAME', 'TABLE', v_unformatted_blocks,
v_unformatted_bytes, v_fs1_blocks, v_fs1_bytes, v_fs2_blocks, v_fs2_bytes,
v_fs3_blocks, v_fs3_bytes, v_fs4_blocks, v_fs4_bytes, v_full_blocks, v_full_bytes);
dbms_output.put_line('Unformatted Blocks = '||v_unformatted_blocks);
dbms_output.put_line('FS1 Blocks = '||v_fs1_blocks);
dbms_output.put_line('FS2 Blocks = '||v_fs2_blocks);
dbms_output.put_line('FS3 Blocks = '||v_fs3_blocks);
dbms_output.put_line('FS4 Blocks = '||v_fs4_blocks);
dbms_output.put_line('Full Blocks = '||v_full_blocks);
end;
/

OUTPUT:

Enter value for schema_name: DBATEST
Enter value for table_name: TEST1
old  15: dbms_space.space_usage ('&SCHEMA_NAME', '&TABLE_NAME', 'TABLE', v_unformatted_blocks,
new  15: dbms_space.space_usage ('DBATEST', 'TEST1', 'TABLE', v_unformatted_blocks,
Unformatted Blocks = 492
FS1 Blocks = 1
FS2 Blocks = 1
FS3 Blocks = 0
FS4 Blocks = 239
Full Blocks = 20584 ----------->>>>>>>>>>>>>>>>>>> THIS MANY BLOCKS ARE FULLLY OCCUPIED

PL/SQL procedure successfully completed.

Where:

unformatted_blocks : Total number of blocks unformatted
fs1_blocks : Number of blocks having at least 0 to 25% free space
fs2_blocks : Number of blocks having at least 25 to 50% free space
fs3_blocks : Number of blocks having at least 50 to 75% free space
fs4_blocks : Number of blocks having at least 75 to 100% free space
ful1_blocks : Total number of blocks full in the segment

Let’s perform some DML operation:

SQL> delete from DBATEST.TEST1;

1450176 rows deleted.

commit;


Check the table size:

SQL> SELECT SUM(BYTES/1024/1024) FROM DBA_SEGMENTS WHERE OWNER='DBATEST' and segment_name='TEST1';

SUM(BYTES/1024/1024)
--------------------
                 168

We can see, table segment size is still same, despite deleting all the rows from the table.

Run the fragmentation script

set serveroutput on
declare
v_unformatted_blocks number;
v_unformatted_bytes number;
v_fs1_blocks number;
v_fs1_bytes number;
v_fs2_blocks number;
v_fs2_bytes number;
v_fs3_blocks number;
v_fs3_bytes number;
v_fs4_blocks number;
v_fs4_bytes number;
v_full_blocks number;
v_full_bytes number;
begin
dbms_space.space_usage ('&SCHEMA_NAME', '&TABLE_NAME', 'TABLE', v_unformatted_blocks,
v_unformatted_bytes, v_fs1_blocks, v_fs1_bytes, v_fs2_blocks, v_fs2_bytes,
v_fs3_blocks, v_fs3_bytes, v_fs4_blocks, v_fs4_bytes, v_full_blocks, v_full_bytes);
dbms_output.put_line('Unformatted Blocks = '||v_unformatted_blocks);
dbms_output.put_line('FS1 Blocks = '||v_fs1_blocks);
dbms_output.put_line('FS2 Blocks = '||v_fs2_blocks);
dbms_output.put_line('FS3 Blocks = '||v_fs3_blocks);
dbms_output.put_line('FS4 Blocks = '||v_fs4_blocks);
dbms_output.put_line('Full Blocks = '||v_full_blocks);
end;
/
Enter value for schema_name: DBATEST
Enter value for table_name: TEST1
old  15: dbms_space.space_usage ('&SCHEMA_NAME', '&TABLE_NAME', 'TABLE', v_unformatted_blocks,
new  15: dbms_space.space_usage ('DBATEST', 'TEST1', 'TABLE', v_unformatted_blocks,
Unformatted Blocks = 492
FS1 Blocks = 0
FS2 Blocks = 0
FS3 Blocks = 0
FS4 Blocks = 20825 ------->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> THIS BLOCKS HAVING 75 TO 100 PERCENT FREE SPACE
Full Blocks = 0

PL/SQL procedure successfully completed.

You can see, 20825 blocks are having 75 percent to 100 percent. This is due to the fragmentation. So this table is a candidate for re-org.

TABLE REORG:

There are multiple methods of reorg , But alter table move is the easiest method to do reorg.

SQL> ALTER TABLE DBATEST.TEST1 move;

Table altered.

Check the Table size:

SQL> SELECT SUM(BYTES/1024/1024) FROM DBA_SEGMENTS WHERE OWNER='DBATEST' and segment_name='TEST1';

SUM(BYTES/1024/1024)
--------------------
               .0625

All the space has been released, Lets run the same fragmentation script

			   
set serveroutput on
declare
v_unformatted_blocks number;
v_unformatted_bytes number;
v_fs1_blocks number;
v_fs1_bytes number;
v_fs2_blocks number;
v_fs2_bytes number;
v_fs3_blocks number;
v_fs3_bytes number;
v_fs4_blocks number;
v_fs4_bytes number;
v_full_blocks number;
v_full_bytes number;
begin
dbms_space.space_usage ('&SCHEMA_NAME', '&TABLE_NAME', 'TABLE', v_unformatted_blocks,
v_unformatted_bytes, v_fs1_blocks, v_fs1_bytes, v_fs2_blocks, v_fs2_bytes,
v_fs3_blocks, v_fs3_bytes, v_fs4_blocks, v_fs4_bytes, v_full_blocks, v_full_bytes);
dbms_output.put_line('Unformatted Blocks = '||v_unformatted_blocks);
dbms_output.put_line('FS1 Blocks = '||v_fs1_blocks);
dbms_output.put_line('FS2 Blocks = '||v_fs2_blocks);
dbms_output.put_line('FS3 Blocks = '||v_fs3_blocks);
dbms_output.put_line('FS4 Blocks = '||v_fs4_blocks);
dbms_output.put_line('Full Blocks = '||v_full_blocks);
end;
/
			   
Enter value for schema_name: DBATEST
Enter value for table_name: TEST1
old  15: dbms_space.space_usage ('&SCHEMA_NAME', '&TABLE_NAME', 'TABLE', v_unformatted_blocks,
new  15: dbms_space.space_usage ('DBATEST', 'TEST1', 'TABLE', v_unformatted_blocks,
Unformatted Blocks = 0
FS1 Blocks = 0
FS2 Blocks = 0
FS3 Blocks = 0
FS4 Blocks = 0
Full Blocks = 0

PL/SQL procedure successfully completed.

NOTE – If indexes are present on this table, then after re-org, rebuild this index, else they will become unusable.

ALTER INDEX INDEX_NAME REBUILD;