REM pctfree_advice.sql REM (c)Go-Faster Consultancy 2008 CREATE TABLE chained_rows ( owner_name varchar2(30), table_name varchar2(30), cluster_name varchar2(30), partition_name varchar2(30), subpartition_name varchar2(30), head_rowid urowid, analyze_timestamp date ); DROP INDEX chained_rows; TRUNCATE TABLE chained_rows; CREATE INDEX chained_rows ON chained_rows (owner_name,table_name) COMPRESS 2 PCTFREE 0; BEGIN FOR x IN ( SELECT owner, table_name, num_rows FROM all_tables t WHERE 1=1 /*------------------------------------------------------- AND NOT table_name IN(SELECT DISTINCT table_name FROM chained_rows) AND num_rows >= 1000000 AND num_rows BETWEEN 100000 AND 1000000 /*--------------------------------------------------------*/ AND temporary = 'N' ) LOOP DELETE FROM chained_rows WHERE owner_name = x.owner AND table_name = x.table_name; EXECUTE IMMEDIATE 'ANALYZE TABLE '||x.owner||'.'||x.table_name||' LIST CHAINED ROWS INTO chained_rows'; END LOOP; END; / COLUMN owner_name FORMAT a8 HEADING 'Owner' COLUMN pct_chained FORMAT 990.9 COLUMN chained_rows HEADING 'Chained|Rows' COLUMN num_rows HEADING 'Number|of Rows' FORMAT 9999999 COLUMN pct_chained HEADING '%Chained' COLUMN pct_Free HEADING '%|Free' FORMAT 999 COLUMN pct_used HEADING '%|Used' FORMAT 999 COLUMN new_pct_free HEADING 'New|%Free' FORMAT 990 COLUMN blocks FORMAT 999990 HEADING 'Blocks' COLUMN avg_space FORMAT 99990 HEADING 'Space|per|Block' COLUMN table_name FORMAT a30 HEADING 'Table Name' COLUMN chain_cnt FORMAT 999999 HEADING 'Chain|Count' COLUMN avg_row_len HEADING 'Average|Row|Length' FORMAT 99999 COLUMN wastage HEADING 'Wastage|(Mb)' FORMAT 9990 spool pctfree_advice SELECT /*+LEADING(c)*/ c.*, t.num_rows , c.chained_rows/t.num_rows*100 pct_chained , t.pct_free, t.pct_used , 100-FLOOR((100-t.pct_free)*(1-c.chained_rows/t.num_rows)) new_pct_free from ( SELECT owner_name, table_name, COUNT(*) chained_rows FROM chained_rows c GROUP BY owner_name, table_name) c , all_tables t WHERE t.owner = c.owner_name AND t.table_name = c.table_name AND t.num_rows > 0 ORDER BY chained_rows desc, 1,2 / SELECT /*+LEADING(c)*/ c.*, t.num_rows , c.chained_rows/t.num_rows*100 pct_chained , t.pct_free, t.pct_used , 100-FLOOR((100-t.pct_free)*(1-c.chained_rows/t.num_rows)) new_pct_free from ( SELECT owner_name, table_name, partition_name, COUNT(*) chained_rows FROM chained_rows c GROUP BY owner_name, table_name, partition_name) c , all_tab_partitions t WHERE t.table_owner = c.owner_name AND t.table_name = c.table_name AND t.partition_name = c.partition_name AND t.num_rows > 0 ORDER BY 1,2,3 / SELECT 'ALTER TABLE '||owner_name||'.'||table_name|| ' MOVE TABLESPACE '||tablespace_name||' PCTFREE 1;' FROM ( SELECT c.owner_name, c.table_name , c.chained_rows/t.num_rows*100 pct_chained , t.pct_free, t.pct_used , 100-FLOOR((100-t.pct_free)*(1-c.chained_rows/t.num_rows)) new_pct_free , t.tablespace_name FROM ( SELECT owner_name, table_name, COUNT(*) chained_rows FROM chained_rows c GROUP BY owner_name, table_name) c , all_tables t WHERE t.owner = c.owner_name AND t.table_name = c.table_name AND t.num_rows > 0 AND c.chained_rows >= 500 ORDER BY c.chained_rows,1,2 ) / SELECT 'ALTER TABLE '||owner_name||'.'||table_name|| ' PCTFREE '||new_pct_free||' /*'||pct_free||'*/ PCTUSED '||LEAST(pct_used,90-new_pct_free)||';' FROM ( SELECT c.owner_name, c.table_name , c.chained_rows/t.num_rows*100 pct_chained , t.pct_free, t.pct_used , 100-FLOOR((100-t.pct_free)*(1-c.chained_rows/t.num_rows)) new_pct_free FROM ( SELECT owner_name, table_name, COUNT(*) chained_rows FROM chained_rows c GROUP BY owner_name, table_name) c , all_tables t WHERE t.owner = c.owner_name AND t.table_name = c.table_name AND t.num_rows > 0 AND c.chained_rows >= 500 ORDER BY c.chained_rows, 1,2 ) / SELECT x.cmd FROM ( SELECT i.* , 'ALTER INDEX '||index_name||' REBUILD TABLESPACE '||tablespace_name||';' cmd FROM user_indexes i WHERE i.tablespace_name IS NOT NULL AND NOT i.index_type = 'LOB' UNION ALL SELECT j.* , 'ALTER INDEX '||index_name||' REBUILD;' FROM user_indexes j WHERE j.tablespace_name IS NULL ) x , ( SELECT owner_name, table_name, COUNT(*) chained_rows FROM chained_rows c GROUP BY owner_name, table_name ) c WHERE c.table_name = x.table_name AND c.owner_name = x.table_owner AND c.chained_rows >= 500 ORDER BY c.chained_rows desc, x.index_name / SELECT 'ALTER INDEX '||index_name||' REBUILD TABLESPACE '||tablespace_name||';' FROM user_indexes WHERE status = 'UNUSABLE' AND tablespace_name IS NOT NULL UNION ALL SELECT 'ALTER INDEX '||index_name||' REBUILD;' FROM user_indexes WHERE status = 'UNUSABLE' and tablespace_name IS NULL / spool off