Odi's astoundingly incomplete notes
New entries | CodeShrink your Oracle database
One thing, Oracle's Enterprise Manager Webapp miserably fails at is executing shrink recommendations that are given by its Automatic Segment Advisor. Here is a little procedure and log table for your SYS schema that does that well.
It shrinks tables and indexes when it saves more than 100MB. It works correctly for tables with function indexes and LOBs. Also for LOBs in securefile storage. It works even for a large number of objects. A failure in one object doesn't prevent work on other objects. It stops during office hours. And you get a nice log of what happened. Run it from a periodic job in your development instance and forget about it.
Interesting that 50 lines of code can do better than the EM.
It shrinks tables and indexes when it saves more than 100MB. It works correctly for tables with function indexes and LOBs. Also for LOBs in securefile storage. It works even for a large number of objects. A failure in one object doesn't prevent work on other objects. It stops during office hours. And you get a nice log of what happened. Run it from a periodic job in your development instance and forget about it.
Interesting that 50 lines of code can do better than the EM.
CREATE TABLE SHRINK_LOG ( DT DATE DEFAULT current_date NOT NULL, TEXT VARCHAR2(4000) ); CREATE OR REPLACE PROCEDURE SYS.SHRINK_DB IS v_err VARCHAR2(4000); v_memento VARCHAR2(4000); BEGIN delete from shrink_log where dt < current_date - 90; insert into shrink_log (text) values ('starting'); commit; for r in (SELECT segment_owner||'.'||segment_name as t, segment_owner as owner, segment_name as table_name, segment_type FROM TABLE (DBMS_SPACE.asa_recommendations ('TRUE', 'FALSE', 'FALSE')) where segment_type IN ('TABLE', 'INDEX') and reclaimable_space > 100E6 order by segment_type desc, reclaimable_space asc) loop -- stop during the day if ((to_number(to_char(current_date, 'HH24')) >= 5) AND (to_number(to_char(current_date, 'HH24')) < 21)) then insert into shrink_log (text) values ('exiting'); commit; return; end if; begin v_memento := NULL; if r.segment_type='TABLE' then for i in (SELECT di.owner||'.'||di.index_name as idx, ie.column_expression FROM dba_indexes di, DBA_IND_EXPRESSIONS ie WHERE di.index_type LIKE 'FUNCTION-BASED%' and di.owner=r.owner and di.table_name=r.table_name and ie.index_owner=di.owner and ie.index_name=di.index_name) loop v_memento := v_memento || 'execute immediate ''create index '|| i.idx ||' on '|| r.t ||' ('|| i.column_expression ||')'';'; execute immediate 'drop index '|| i.idx; end loop; execute immediate 'alter table '|| r.t ||' enable row movement'; execute immediate 'alter table '|| r.t ||' shrink space cascade'; for l in (select l.column_name, l.tablespace_name from dba_segments s, dba_lobs l where s.segment_name = l.segment_name and s.tablespace_name = l.tablespace_name and s.owner = r.owner and l.table_name = r.table_name and s.segment_type='LOBSEGMENT' and s.segment_subtype='SECUREFILE') loop execute immediate 'ALTER TABLE '|| r.t ||' MOVE LOB('|| l.column_name ||') STORE AS (TABLESPACE '|| l.tablespace_name ||')'; end loop; execute immediate 'alter table '|| r.t ||' disable row movement'; else -- INDEX execute immediate 'alter index '|| r.t ||' shrink space compact cascade'; end if; if v_memento is not null then execute immediate 'begin '|| v_memento ||' end'; end if; -- rebuild any UNUSABLE indexes again for r in (select * from dba_indexes where status!='VALID' and index_type='NORMAL' and partitioned='NO') loop execute immediate 'alter index '|| r.owner ||'.'|| r.index_name ||' rebuild'; end loop; insert into shrink_log (text) values (r.t||': OK'); exception when others then v_err := substr(SQLERRM, 1, 3900); insert into shrink_log (text) values (r.t||': '||v_Err); end; commit; end loop; insert into shrink_log (text) values ('complete'); commit; END SHRINK_DB; /
Here is a good explanation how to reclaim the wasted space in a segment
http://dbpilot.net/2018/02/14/reclaiming-wasted-space-in-a-segment/