Documente Academic
Documente Profesional
Documente Cultură
1)
This reports the size (in MB) consumed by the table and associated indexes, LOBs and
LOB indexes, with columns for the total size (Total_MB), table size (Table_MB), indexes
size (Indexes_MB), LOB Size (LOB_MB) and LOB Indexes size (LOBIndexes_MB). It also
indicates if the table is an Oracle E-Business Suite table and it's description (if
available).
Indexes and LOBs are included because they can often consume a large amount of
space, sometimes more than the base table.
The script could be altered to suit different needs. It currently excludes all tables where
the total size is below 100MB; many customers will want to set this much higher.
This script is provided for educational purposes only and not supported by Oracle
Support Services.
COLUMN application_name FORMAT A30
COLUMN owner FORMAT A10
COLUMN table_name FORMAT A30
COLUMN Total_MB FORMAT 999,999,990
COLUMN Table_MB FORMAT 999,999,990
COLUMN Indexes_MB FORMAT 999,999,990
COLUMN LOB_MB FORMAT 999,999,990
COLUMN LOBIndexes_MB FORMAT 999,999,990
COLUMN oracle_id FORMAT 9999
COLUMN EBS_App FORMAT A11
COLUMN description FORMAT A60
--EBS Apps
WITH tab_size AS
(SELECT owner, table_name, SUM(total_bytes) total_bytes, SUM(tab_bytes) tab_bytes,
SUM(ind_bytes) ind_bytes,
SUM(lob_bytes) lob_bytes, SUM(lobind_bytes) lobind_bytes
FROM
(SELECT owner, segment_name table_name, bytes total_bytes, bytes tab_bytes, 0 ind_bytes, 0
lob_bytes, 0 lobind_bytes
FROM dba_segments
WHERE segment_type IN ('TABLE', 'TABLE PARTITION', 'TABLE SUBPARTITION')
UNION ALL
SELECT i.table_owner owner, i.table_name, s.bytes total_bytes, 0 tab_bytes, s.bytes
ind_bytes, 0 lob_bytes, 0 lobind_bytes
FROM dba_indexes i, dba_segments s
WHERE s.segment_name = i.index_name
AND s.owner = i.owner
AND s.segment_type IN ('INDEX', 'INDEX PARTITION', 'INDEX SUBPARTITION')
UNION ALL
SELECT l.owner, l.table_name, s.bytes total_bytes, 0 tab_bytes, 0 ind_bytes, s.bytes
lob_bytes, 0 lobind_bytes
FROM dba_lobs l, dba_segments s
WHERE s.segment_name = l.segment_name
AND s.owner = l.owner
AND s.segment_type IN ('LOBSEGMENT', 'LOB PARTITION')
UNION ALL
SELECT l.owner, l.table_name, s.bytes total_bytes, 0 tab_bytes, 0 ind_bytes, 0 lob_bytes,
s.bytes lobind_bytes
FROM dba_lobs l, dba_segments s
WHERE s.segment_name = l.index_name
AND s.owner = l.owner
AND s.segment_type = 'LOBINDEX')
GROUP BY owner, table_name)
SELECT fa.application_name,
t.owner,
t.table_name,
ROUND(t.total_bytes/(1024*1024),0) Total_MB,
ROUND(t.tab_bytes/(1024*1024),0) Table_MB,
ROUND(t.ind_bytes/(1024*1024),0) Indexes_MB,
ROUND(t.lob_bytes/(1024*1024),0) LOB_MB,
ROUND(t.lobind_bytes/(1024*1024),0) LOBIndexes_MB,
fou.oracle_id,
'EBS App' EBS_App,
ft.description
FROM tab_size t,
fnd_oracle_userid fou,
fnd_product_installations fpi,
fnd_tables ft,
fnd_application_tl fa
WHERE fou.oracle_username = t.owner
AND fpi.oracle_id = fou.oracle_id
AND ft.table_name = t.table_name
AND fpi.application_id = ft.application_id
AND fa.application_id = ft.application_id
AND fa.language = 'US'
AND t.total_bytes IS NOT NULL
AND t.total_bytes >= 104857600
UNION ALL
--Not EBS Apps
SELECT fa.application_name,
t.owner,
t.table_name,
ROUND(t.total_bytes/(1024*1024),0) Total_MB,
ROUND(t.tab_bytes/(1024*1024),0) Table_MB,
ROUND(t.ind_bytes/(1024*1024),0) Indexes_MB,
ROUND(t.lob_bytes/(1024*1024),0) LOB_MB,
ROUND(t.lobind_bytes/(1024*1024),0) LOBIndexes_MB,
fou.oracle_id,
'Not EBS App' EBS_App,
ft.description
FROM tab_size t,
fnd_oracle_userid fou,
fnd_product_installations fpi,
fnd_tables ft,
fnd_application_tl fa
WHERE fou.oracle_username (+) = t.owner
AND fpi.oracle_id (+) = fou.oracle_id
AND ft.table_name (+) = t.table_name
AND fpi.application_id (+) = ft.application_id
AND fa.application_id (+) = ft.application_id
AND fa.language (+) = 'US'
AND t.owner != 'SYS'
AND ft.application_id IS NULL
AND t.total_bytes IS NOT NULL
AND t.total_bytes >= 104857600
ORDER BY 4 DESC;
Example Output:
APPLICATION_NAME OWNER TABLE_NAME TOTAL_MB
TABLE_MB INDEXES_MB LOB_MB LOBINDEXES_MB ORACLE_ID EBS_APP DESCRIPTION
------------------------------ ---------- ------------------------------ ------------
------------ ------------ ------------ ------------- --------- -----------
------------------------------------------------------------
Application Object Library APPLSYS FND_LOBS 2,042,304
15,133 7,548 2,019,623 0 0 EBS App LOBs being managed by the
Generic File Manager
Subledger Accounting XLA XLA_DISTRIBUTION_LINKS 1,135,177
739,249 395,928 0 0 602 EBS App The XLA_DISTRIBUTION_LINKS
table stores the link between tra
d receivables
CRM Foundation JTF JTF_NOTES_TL 527,767
279,409 39,332 208,865 161 690 EBS App Translated base table for
Notes module.
Configurator CZ CZ_CONFIG_ITEMS 493,713
241,234 252,478 0 0 708 EBS App Items selected for the
configuration
Subledger Accounting XLA XLA_AE_LINES 454,312
301,143 153,169 0 0 602 EBS App The XLA_AE_LINES table
stores the subledger journal entry li
le languages
E-Business Tax ZX ZX_LINES 148,854
102,476 46,378 0 0 235 EBS App This table stores detail
tax lines for transactions of multi
A SQL script similar to the following (or parts of it) could be used to identify tables
where particular reclaim methods cannot be used.
These scripts are provided for educational purposes only and not supported by Oracle
Support Services.
SELECT
t.owner,
t.table_name,
DECODE(ts.extent_management
,'LOCAL'
,DECODE(ts.segment_space_management
,'AUTO'
,''
,'Tablespace '||t.tablespace_name||' does not have automatic segment space
management')
,'Tablespace '||t.tablespace_name||' not a locally managed tablespace') property
,'SHRINK SPACE cannot be used' limitation
FROM dba_tables t,
dba_tablespaces ts
WHERE ts.tablespace_name = t.tablespace_name
AND (ts.extent_management != 'LOCAL' OR ts.segment_space_management != 'AUTO')
UNION ALL
SELECT
b.owner,
b.master table_name,
'MV '||r.owner||'.'||r.name||' has ROWID refresh method' property,
'SHRINK SPACE does not work. REDEFINITION cannot be used with ROWID method' limitation
FROM dba_registered_mviews r,
dba_base_table_mviews b
WHERE r.mview_id = b.mview_id
AND b.owner != 'APPS'
AND b.master NOT LIKE '%MV%'
AND r.refresh_method = 'ROWID'
UNION ALL
SELECT
i.table_owner owner,
i.table_name,
'Index '||i.owner||'.'||i.index_name||' is a '||i.index_type||' index' property,
'SHRINK SPACE cannot be used' limitation
FROM dba_indexes i
WHERE (i.index_type like '%FUNCTION%' OR i.index_type IN ('BITMAP','DOMAIN'))
UNION ALL
SELECT
t.owner,
t.table_name,
'Table uses '||compress_for||' compression' property,
'SHRINK SPACE cannot be used' limitation
FROM dba_tables t
WHERE t.compress_for IS NOT NULL
AND t.compress_for != 'ADVANCED'
AND t.compression != 'DISABLED'
UNION ALL
SELECT
c.owner,
c.table_name,
'Column '||c.column_name||' has '||c.data_type||' data type' property,
DECODE(c.data_type
,'LONG'
,'SHRINK SPACE, ALTER TABLE ... MOVE, REDEFINITION and CTAS cannot be used'
,DECODE(c.data_type
,'LONG RAW'
,'ALTER TABLE ... MOVE and REDEFINITION cannot be used'
,'REDEFINITION cannot be used')) limitation
FROM all_tab_columns c,
all_objects o
WHERE (c.data_type IN ('LONG','LONG RAW','BFILE'))
AND o.owner = c.owner
AND o.object_name = c.table_name
AND o.object_type != 'VIEW'
UNION ALL
SELECT
c.owner,
c.table_name,
'Column '||c.column_name||' has user defined data type '||c.data_type||', which contains a
LONG data type' property,
'SHRINK SPACE, ALTER TABLE ... MOVE, REDEFINITION and CTAS cannot be used' limitation
FROM all_tab_columns c,
all_source s,
all_objects o
WHERE o.owner = c.owner
AND o.object_name = c.table_name
AND o.object_type != 'VIEW'
AND s.owner = c.data_type_owner
AND s.name = c.data_type
AND s.type = 'TYPE'
AND UPPER(s.text) like '%LONG%'
UNION ALL
SELECT
c.owner,
c.table_name,
'Column '||c.column_name||' has user defined data type '||c.data_type||', which contains a
LONG RAW data type' property,
'ALTER TABLE ... MOVE and REDEFINITION cannot be used' limitation
FROM all_tab_columns c,
all_source s,
all_objects o
WHERE o.owner = c.owner
AND o.object_name = c.table_name
AND o.object_type != 'VIEW'
AND s.owner = c.data_type_owner
AND s.name = c.data_type
AND s.type = 'TYPE'
AND UPPER(s.text) like '%LONG RAW%'
UNION ALL
SELECT
c.owner,
c.table_name,
'Column '||c.column_name||' has user defined data type '||c.data_type||', which contains a
BFILE data type' property,
'REDEFINITION cannot be used' limitation
FROM all_tab_columns c,
all_source s,
all_objects o
WHERE o.owner = c.owner
AND o.object_name = c.table_name
AND o.object_type != 'VIEW'
AND s.owner = c.data_type_owner
AND s.name = c.data_type
AND s.type = 'TYPE'
AND UPPER(s.text) like '%BFILE%'
UNION ALL
SELECT
tr.table_owner owner,
tr.table_name,
tr.owner||'.'||tr.trigger_name||' is a ROWID trigger' property,
'SHRINK SPACE cannot be used' limitation
FROM dba_triggers tr
WHERE UPPER(when_clause) like '%ROW%'
UNION ALL
SELECT
t.owner,
t.table_name,
'Index Organized Table. '||DECODE(t.iot_name,NULL,' ','The Parent Table is '||t.iot_name)
property,
'SHRINK SPACE cannot be used' limitation
FROM dba_tables t
WHERE t.iot_type IS NOT NULL
UNION ALL
SELECT
b.owner,
b.master table_name,
'MV '||r.owner||'.'||r.name||' has COMMIT refresh mode' property,
'SHRINK SPACE cannot be used' limitation
FROM dba_registered_mviews r,
dba_base_table_mviews b,
dba_mviews m
WHERE r.mview_id = b.mview_id
AND r.name = m.mview_name
AND m.refresh_mode = 'COMMIT'
UNION ALL
SELECT
l.owner,
l.table_name,
'Column '||l.column_name||' is a securefile LOB' property,
'SHRINK SPACE cannot be used' limitation
FROM all_lobs l
WHERE l.securefile = 'YES'
UNION ALL
SELECT
t.owner,
t.table_name,
'Table is in cluster '||t.cluster_owner||'.'||t.cluster_name property,
'SHRINK SPACE cannot be used' limitation
FROM all_tables t WHERE cluster_name is NOT NULL
UNION ALL
SELECT
n.owner,
n.parent_table_name table_name,
'Has nested table(s) in colum(s) '||n.parent_table_column property,
'REDEFINITION cannot be used' limitation
FROM dba_nested_tables n
-- Don't forget to also check for tables that use Oracle Label Security, Fine Grained Access
Control (Database Vault), Oracle Real Application Security or Flashback Data Archive
-- If these are installed and used on EBS tables
ORDER BY 1, 2
-- The following gives all those tables which do not have a primary or pseudo primary key (unique
index where all columns are not nullable).
-- and so cannot be effectively used for Online Redefinition:
WITH
null_indcol
AS (SELECT DISTINCT dic.index_owner, dic.index_name
FROM dba_ind_columns dic,
dba_tab_columns dtc
WHERE dtc.owner = dic.table_owner
AND dtc.table_name = dic.table_name
AND dtc.column_name = dic.column_name
AND dtc.nullable = 'Y')
,ui_tab
AS (SELECT DISTINCT t.table_owner, t.table_name
FROM
(SELECT i.table_owner, i.table_name, i.index_owner, i.index_name
FROM
(SELECT dc.owner table_owner, dc.table_name, dc.index_owner, dc.index_name
FROM dba_constraints dc
WHERE dc.constraint_type IN ('P','U')
AND dc.status = 'ENABLED'
UNION
SELECT di.table_owner, di.table_name, di.owner index_owner, di.index_name
FROM dba_indexes di
WHERE di.uniqueness = 'UNIQUE'
AND di.status = 'VALID') i
WHERE NOT EXISTS (SELECT 'exists'
FROM null_indcol ni
WHERE ni.index_owner = i.index_owner
AND ni.index_name = i.index_name)
)
t)
SELECT t.owner,
t.table_name,
'Table has no primary key or pseudo primary key (unique and all Cols NOT NULL)',
'REDEFINITION cannot effectively be used' limitation
FROM dba_tables t
WHERE owner NOT IN ('SYS')
AND NOT EXISTS (SELECT 'exists'
FROM ui_tab ui
WHERE ui.table_owner = t.owner
AND ui.table_name = t.table_name)
ORDER BY 1,2;
Appendix D – SQL to determine how much data will be purged and impact on blocks / extents
This script is provided for educational purposes only and not supported by Oracle
Support Services.
COLUMN num_rows FORMAT 9,999,999,990 HEADING 'Num Rows'
COLUMN num_rows_will_purge FORMAT 9,999,999,990 HEADING 'Num Rows|Will Purge'
COLUMN num_rows_no_purge FORMAT 9,999,999,990 HEADING 'Num Rows|Will Not'
COLUMN num_blocks FORMAT 9,999,999,990 HEADING 'Num Blocks'
COLUMN blks_willbe_empty FORMAT 9,999,999,990 HEADING 'Num Blocks|Will be|Empty'
COLUMN blks_willbe_unaffect FORMAT 9,999,999,990 HEADING 'Num Blocks|Will be|Unaffected'
COLUMN blks_willbe_part_empty FORMAT 9,999,999,990 HEADING 'Num Blocks|Will be|Part Empty'
COLUMN blks_already_empty FORMAT 9,999,999,990 HEADING 'Num Blocks|Already|Empty'
COLUMN num_extents FORMAT 9,999,999,990 HEADING 'Num Extents'
COLUMN exts_willbe_empty FORMAT 9,999,999,990 HEADING 'Num Extents|Will be|Empty'
COLUMN exts_willbe_unaffect FORMAT 9,999,999,990 HEADING 'Num Extents|Will be|Unaffected'
COLUMN exts_willbe_partial FORMAT 9,999,999,990 HEADING 'Num Extents|Will be|Part Empty'
COLUMN exts_already_empty FORMAT 9,999,999,990 HEADING 'Num Extents|Already|Empty'
COLUMN will_purge_pct FORMAT 90.00 HEADING 'Percent|Will Purge'
COLUMN no_purge_pct FORMAT 90.00 HEADING 'Percent|Will Not'
COLUMN empty_block_pct FORMAT 90.00 HEADING 'Pct Blks|Will be|Empty'
COLUMN unaffect_block_pct FORMAT 90.00 HEADING 'Pct Blks|Will be|Unaffected'
COLUMN part_block_pct FORMAT 90.00 HEADING 'Pct Blks|Will be|Part Empty'
COLUMN already_empty_block_pct FORMAT 90.00 HEADING 'Pct Blks|Already|Empty'
COLUMN empty_ext_pct FORMAT 90.00 HEADING 'Pct Exts|Will be|Empty'
COLUMN unaffect_ext_pct FORMAT 90.00 HEADING 'Pct Exts|Will be|Unaffected'
COLUMN part_ext_pct FORMAT 90.00 HEADING 'Pct Exts|Will be|Part Empty'
COLUMN already_empty_ext_pct FORMAT 90.00 HEADING 'Pct Exts|Already|Empty'
WITH block_expander
AS
(SELECT rownum block_num
FROM inv.mtl_material_transactions -- change for other tables -- this is just
to get a (potential) row for each block in an extent
WHERE rownum <=
(SELECT MAX(blocks)
FROM dba_extents e
WHERE e.owner = 'INV' -- change owner for other tables
AND e.segment_name = 'MTL_MATERIAL_TRANSACTIONS')) -- change for other tables
SELECT
SUM(num_rows) num_rows,
SUM(num_purge_rows) num_rows_will_purge,
SUM(num_no_purge_rows) num_rows_no_purge,
SUM(num_blocks) num_blocks,
SUM(num_empty_blk) blks_willbe_empty,
SUM(num_unaffect_blk) blks_willbe_unaffect,
SUM(num_partial_blk) blks_willbe_part_empty,
SUM(num_already_empty_blk) blks_already_empty,
COUNT(*) num_extents,
SUM(DECODE(num_blocks-num_empty_blk-num_already_empty_blk,0,1,0) * SIGN(num_empty_blk))
exts_willbe_empty,
-- i.e. if (num_blocks = num_empty_blk + num_already_empty_blk) and num_empty_blk > 0
SUM(DECODE(num_blocks-num_unaffect_blk-num_already_empty_blk,0,1,0)*SIGN(num_unaffect_blk))
exts_willbe_unaffect,
-- i.e. if (num_blocks = num_unaffect_blk + num_already_empty_blk) and num_unaffect_blk > 0
SUM(SIGN(SIGN(num_partial_blk) + (SIGN(num_empty_blk)*SIGN(num_unaffect_blk))))
exts_willbe_partial,
-- i.e. if num_partial_blk > 0 OR (num_empty_blk > 0 and num_unaffect_blk > 0)
SUM(DECODE(num_blocks, num_already_empty_blk, 1,0)) exts_already_empty,
-- i.e. if num_already_empty_blk = num_blocks
SUM(num_rows) num_rows,
ROUND(SUM(num_purge_rows)*100/SUM(num_rows),1) will_purge_pct,
ROUND(SUM(num_no_purge_rows)*100/SUM(num_rows),1) no_purge_pct,
SUM(num_blocks) num_blocks,
ROUND(SUM(num_empty_blk)*100/SUM(num_blocks),1) empty_block_pct,
ROUND(SUM(num_unaffect_blk)*100/SUM(num_blocks),1) unaffect_block_pct,
ROUND(SUM(num_partial_blk)*100/SUM(num_blocks),1) part_block_pct,
ROUND(SUM(num_already_empty_blk)*100/SUM(num_blocks),1) already_empty_block_pct,
COUNT(*) num_extents,
ROUND(SUM(DECODE(num_blocks-num_empty_blk-num_already_empty_blk,0,1,0) *
SIGN(num_empty_blk))*100/COUNT(*),1) empty_ext_pct,
ROUND(SUM(DECODE(num_blocks-num_unaffect_blk-
num_already_empty_blk,0,1,0)*SIGN(num_unaffect_blk))*100/COUNT(*),1) unaffect_ext_pct,
ROUND(SUM(SIGN(SIGN(num_partial_blk) +
(SIGN(num_empty_blk)*SIGN(num_unaffect_blk))))*100/COUNT(*),1) part_ext_pct,
ROUND(SUM(DECODE(num_blocks, num_already_empty_blk, 1,0))*100/COUNT(*),1)
already_empty_ext_pct
FROM
(SELECT
relative_fno,
extent_id,
SUM(row_count) num_rows,
SUM(purge_count) num_purge_rows,
SUM(no_purge_count) num_no_purge_rows,
COUNT(*) num_blocks,
SUM(DECODE(row_count-purge_count,0,1,0)) num_empty_blk,
-- i.e. purge_count = row_count
SUM(DECODE(row_count-no_purge_count,0,1,0)) num_unaffect_blk,
-- i.e. no_purge_count = row_count
SUM(DECODE(SIGN(purge_count)+SIGN(no_purge_count),2,1,0)) num_partial_blk,
-- i.e. purge_count > 0 and no_purge_count > 0
SUM(DECODE(row_count,NULL,1,0)) num_already_empty_blk
-- i.e. row_count is NULL - indicating that no row returned in DAT inline query.
FROM
(SELECT /*+ leading(dat) */
e.relative_fno,
e.extent_id,
e.block_id,
dat.row_count,
dat.purge_count,
dat.no_purge_count
FROM
(SELECT /*+ parallel(t) full(t) */
dbms_rowid.rowid_to_absolute_fno(t.rowid, 'INV','MTL_MATERIAL_TRANSACTIONS')
rel_fno, -- change owner, table name for other tables
dbms_rowid.rowid_block_number(t.rowid) block,
COUNT(*) row_count,
SUM(DECODE(p.open_flag,'N',(DECODE(SIGN(p.period_year-2006),-1,1,0)),0))
purge_count, -- for other tables replace these clauses
SUM(DECODE(NVL(open_flag,'Y'),'Y',1,(DECODE(SIGN(p.period_year-2006),-1,0,1))))
no_purge_count -- with the purge criteria
FROM inv.mtl_material_transactions t, -- for other tables replace table list
inv.org_acct_periods p -- for other tables replace table list
WHERE p.acct_period_id (+) = t.acct_period_id -- for other tables replace join
GROUP BY dbms_rowid.rowid_to_absolute_fno(t.rowid,
'INV','MTL_MATERIAL_TRANSACTIONS'), -- change owner and table name for other tables
dbms_rowid.rowid_block_number(t.rowid)
) dat,
(SELECT
ei.tablespace_name,
ei.owner,
ei.segment_name,
ei.relative_fno,
ei.extent_id,
ei.block_id+b.block_num-1 block_id,
ei.block_id extent_start_block,
ei.block_id+blocks-1 extent_end_block,
ei.blocks extent_blocks,
ei.bytes/ei.blocks bytes_in_block
FROM
dba_extents ei,
block_expander b
WHERE ei.owner = 'INV' -- change owner for other tables
AND ei.segment_name = 'MTL_MATERIAL_TRANSACTIONS'
-- change for other tables
AND b.block_num <= ei.blocks) e
WHERE e.relative_fno = dat.rel_fno (+)
AND e.block_id = dat.block (+)
)
GROUP BY relative_fno, extent_id
);
Example Output:
Num Blocks Num Blocks Num
Blocks Num Blocks Num Extents Num Extents Num Extents Num Extents
Num Rows Num Rows Will be Will be
Will be Already Will be Will be Will be Already
Num Rows Will Purge Will Not Num Blocks Empty Unaffected
Part Empty Empty Num Extents Empty Unaffected Part Empty
Empty
-------------- -------------- -------------- -------------- -------------- --------------
-------------- -------------- -------------- -------------- -------------- --------------
--------------
3,010,819 1,005,306 2,005,513 146,448 42,283 95,123
7,726 1,316 9,153 1,019 5,690 2,444 0
Pct Blks Pct Blks Pct Blks Pct Blks
Pct Exts Pct Exts Pct Exts Pct Exts
Percent Percent Will be Will be Will be Already
Will be Will be Will be Already
Num Rows Will Purge Will Not Num Blocks Empty Unaffected Part Empty Empty Num
Extents Empty Unaffected Part Empty Empty
-------------- ---------- -------- -------------- -------- ---------- ---------- --------
-------------- -------- ---------- ---------- --------
3,010,819 33.40 66.60 146,448 28.90 65.00 5.30 0.90
9,153 11.10 62.20 26.70 0.00
These scripts are provided for educational purposes only and not supported by Oracle
Support Services.
Table (ASSM)
For segments using Automatic Segment Space Management (ASSM) use the following
to identify the space available on a table.
DECLARE
l_unformatted_blocks NUMBER;
l_unformatted_bytes NUMBER;
l_fs1_blocks NUMBER;
l_fs1_bytes NUMBER;
l_fs2_blocks NUMBER;
l_fs2_bytes NUMBER;
l_fs3_blocks NUMBER;
l_fs3_bytes NUMBER;
l_fs4_blocks NUMBER;
l_fs4_bytes NUMBER;
l_full_blocks NUMBER;
l_full_bytes NUMBER;
BEGIN
DBMS_SPACE.SPACE_USAGE(segment_owner => 'INV',
segment_name => 'MTL_MATERIAL_TRANSACTIONS',
segment_type => 'TABLE',
unformatted_blocks => l_unformatted_blocks,
unformatted_bytes => l_unformatted_bytes,
fs1_blocks => l_fs1_blocks,
fs1_bytes => l_fs1_bytes,
fs2_blocks => l_fs2_blocks,
fs2_bytes => l_fs2_bytes,
fs3_blocks => l_fs3_blocks,
fs3_bytes => l_fs3_bytes,
fs4_blocks => l_fs4_blocks,
fs4_bytes => l_fs4_bytes,
full_blocks => l_full_blocks,
full_bytes => l_full_bytes);
Unformatted Blocks = 0
Unformatted Bytes = 0
FS1 Blocks (0-25%) = 1020
FS2 Blocks (25-50%) = 1821
FS3 Blocks (50-75%) = 2614
FS4 Blocks (75-100%) = 43980
Full Blocks = 90996
FS1 Bytes (0-25%) = 8355840
FS2 Bytes (25-50%) = 14917632
FS3 Bytes (50-75%) = 21413888
FS4 Bytes (75-100%) = 360284160
Full Bytes = 745439232
Where
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
FS1_Bytes = Number of bytes in blocks having at least 0 to 25% free space
FS2_Bytes = Number of bytes in blocks having at least 25 to 50% free space
FS3_Bytes = Number of bytes in blocks having at least 50 to 75% free space
FS4_Bytes = Number of bytes in blocks having at least 75 to 100% free space
Full_Bytes Total number of bytes in full blocks in the segment
Indexes (ASSM)
For segments using Automatic Segment Space Management (ASSM) the following can
be used to identify the space available on indexes for a table:
DECLARE
l_unformatted_blocks NUMBER;
l_unformatted_bytes NUMBER;
l_fs1_blocks NUMBER;
l_fs1_bytes NUMBER;
l_fs2_blocks NUMBER;
l_fs2_bytes NUMBER;
l_fs3_blocks NUMBER;
l_fs3_bytes NUMBER;
l_fs4_blocks NUMBER;
l_fs4_bytes NUMBER;
l_full_blocks NUMBER;
l_full_bytes NUMBER;
CURSOR c_indexes
IS
SELECT
owner,
index_name
FROM dba_indexes
WHERE table_owner = 'INV'
AND table_name = 'MTL_MATERIAL_TRANSACTIONS'
ORDER BY index_name;
BEGIN
FOR r_index IN c_indexes LOOP
l_unformatted_blocks := NULL;
l_unformatted_bytes := NULL;
l_fs1_blocks := NULL;
l_fs1_bytes := NULL;
l_fs2_blocks := NULL;
l_fs2_bytes := NULL;
l_fs3_blocks := NULL;
l_fs3_bytes := NULL;
l_fs4_blocks := NULL;
l_fs4_bytes := NULL;
l_full_blocks := NULL;
l_full_bytes := NULL;
DBMS_OUTPUT.PUT_LINE('Owner = '||r_index.owner);
DBMS_OUTPUT.PUT_LINE('Index = '||r_index.index_name);
DBMS_OUTPUT.PUT_LINE('Unformatted Bytes = '||l_unformatted_bytes);
DBMS_OUTPUT.PUT_LINE('FS1 Blocks (0-25%) = '||l_fs1_blocks);
DBMS_OUTPUT.PUT_LINE('FS2 Blocks (25-50%) = '||l_fs2_blocks);
DBMS_OUTPUT.PUT_LINE('FS3 Blocks (50-75%) = '||l_fs3_blocks);
DBMS_OUTPUT.PUT_LINE('FS4 Blocks (75-100%) = '||l_fs4_blocks);
DBMS_OUTPUT.PUT_LINE('Full Blocks = '||l_full_blocks);
DBMS_OUTPUT.PUT_LINE('FS1 Bytes (0-25%) = '||l_fs1_bytes);
DBMS_OUTPUT.PUT_LINE('FS2 Bytes (25-50%) = '||l_fs2_bytes);
DBMS_OUTPUT.PUT_LINE('FS3 Bytes (50-75%) = '||l_fs3_bytes);
DBMS_OUTPUT.PUT_LINE('FS4 Bytes (75-100%) = '||l_fs4_bytes);
DBMS_OUTPUT.PUT_LINE('Full Bytes = '||l_full_bytes);
END LOOP;
END;
/
Owner = INV
Index = MTL_MATERIAL_TRANSACTIONS_N1
Unformatted Bytes = 0
FS1 Blocks (0-25%) = 0
FS2 Blocks (25-50%) = 3495
FS3 Blocks (50-75%) = 0
FS4 Blocks (75-100%) = 0
Full Blocks = 8879
FS1 Bytes (0-25%) = 0
FS2 Bytes (25-50%) = 28631040
FS3 Bytes (50-75%) = 0
FS4 Bytes (75-100%) = 0
Full Bytes = 72736768
Owner = INV
Index = MTL_MATERIAL_TRANSACTIONS_N10
Unformatted Bytes = 0
FS1 Blocks (0-25%) = 0
FS2 Blocks (25-50%) = 85
FS3 Blocks (50-75%) = 0
FS4 Blocks (75-100%) = 0
Full Blocks = 3269
FS1 Bytes (0-25%) = 0
FS2 Bytes (25-50%) = 696320
FS3 Bytes (50-75%) = 0
FS4 Bytes (75-100%) = 0
Full Bytes = 26779648
....
For segments not using Automatic Segment Space Management (ASSM) then use the
following to identify the space available on a table:
DECLARE
l_free_blocks NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE('FREE_BLOCKS = '||l_free_blocks);
END;
/
FREE_BLOCKS = 135581
For segments not using Automatic Segment Space Management (ASSM) the following
can be used to identify the space available on indexes for a table:
DECLARE
l_free_blocks NUMBER;
CURSOR c_indexes
IS
SELECT owner, index_name
FROM dba_indexes
WHERE table_owner = 'AR'
AND table_name = 'HZ_IMP_PARTIES_INT'
ORDER BY index_name;
BEGIN
FOR r_index IN c_indexes LOOP
l_free_blocks := NULL;
DBMS_OUTPUT.PUT_LINE('Owner = '||r_index.owner);
DBMS_OUTPUT.PUT_LINE('Index = '||r_index.index_name);
DBMS_OUTPUT.PUT_LINE('FREE_BLOCKS = '||l_free_blocks);
END LOOP;
END;
/
Owner = AR
Index = HZ_IMP_PARTIES_INT_U1
FREE_BLOCKS = 11
...
These scripts are provided for educational purposes only and not supported by Oracle
Support Services.
-- Create the interim table - omit the PARALLEL modifier and hints to do it in serial.
-- All Triggers that existed on MTL_MATERIAL_TRANSACTIONS will now need to be created on the new
version of MTL_MATERIAL_TRANSACTIONS
... These are not included in this example script.
GRANT ALTER, DELETE, INDEX, INSERT, SELECT, UPDATE, REFERENCES, READ, ON COMMIT REFRESH, QUERY
REWRITE, DEBUG, FLASHBACK ON INV.MTL_MATERIAL_TRANSACTIONS TO APPS WITH GRANT OPTION;
CONNECT APPS;
The command can also be run without the CASCADE to restrict the shrink operation to
the table and not cascade to the indexes.
Online Table Re-Definition
From database version 12.1 this can be done using a single procedure as follows:
-- Note that the REDEF_TABLE procedure requires the table_part_tablespace and index_tablespace
tablespaces to be specified. Use the same tablespace(s) that the tables and indexes are currently
in.
BEGIN
-- create interim table - exactly the same as original -- but omit NOT NULL constraints (these
will be copied in the COPY_TABLE_DEPENDENTS procedure)
CREATE TABLE INV.MTL_MATERIAL_TRANSACTIONS_INT
(TRANSACTION_ID NUMBER,
LAST_UPDATE_DATE DATE,
LAST_UPDATED_BY NUMBER,
CREATION_DATE DATE,
... and all the remaining columns
);
dbms_output.put_line('Num_errors :'||TO_CHAR(l_num_errors));
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('Num_errors :'||TO_CHAR(l_num_errors));
dbms_output.put_line('Exception raised');
dbms_output.put_line(SQLCODE);
dbms_output.put_line(SQLERRM);
RAISE;
END;
/
-- Synchronize the tables - this could be carried out multiple times between START_REDEF_TABLE
and FINISH_REDEF_TABLE
BEGIN
DBMS_REDEFINITION.SYNC_INTERIM_TABLE(uname => 'INV',
orig_table 'MTL_MATERIAL_TRANSACTIONS',
int_table => 'MTL_MATERIAL_TRANSACTIONS_INT',
part_name => NULL);
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('Exception raised');
dbms_output.put_line(SQLCODE);
dbms_output.put_line(SQLERRM);
RAISE;
END;
/
Rebuild Indexes
-- Rebuild the indexes - omit the ONLINE modifier to do it offline and the PARALLEL modifier to
do it in serial.
ALTER INDEX INV.MTL_MATERIAL_TRANSACTIONS_N1 REBUILD ONLINE PARALLEL;
ALTER INDEX INV.MTL_MATERIAL_TRANSACTIONS_N10 REBUILD ONLINE PARALLEL;
ALTER INDEX INV.MTL_MATERIAL_TRANSACTIONS_N11 REBUILD ONLINE PARALLEL;
... and all the other indexes.
These scripts are provided for educational purposes only and not supported by Oracle
Support Services.
Prior to reclaiming space from segment(s), identify the datafiles where the segment(s)
reside. This includes the indexes, LOBs etc, as well as the main table. This will give the
datafiles where space could be available after reclaim.
Note that for large tables, depending on the tablespace model used, this could be a
large number of data files.
-- this will give the datafiles used by the segment and its indexes and the space used
SELECT e.tablespace_name,
e.relative_fno,
f.file_name,
COUNT(*) extents,
SUM(e.blocks) blocks,
SUM(e.bytes) bytes -- this is just the number of blocks * blocksize
FROM dba_extents e,
dba_data_files f
WHERE ((e.owner = 'AR' AND e.segment_name = 'RA_CUST_TRX_LINE_GL_DIST_ALL')
OR (e.owner, e.segment_name) IN
(SELECT i.owner, i.index_name
FROM all_indexes i
WHERE i.table_owner = 'AR'
AND i.table_name = 'RA_CUST_TRX_LINE_GL_DIST_ALL')
) -- this can be replaced by different segment criteria
AND f.tablespace_name = e.tablespace_name
AND f.relative_fno = e.relative_fno
GROUP BY e.tablespace_name, e.relative_fno, f.file_name
ORDER BY e.tablespace_name, e.relative_fno, f.file_name;
-- this will give the space used on each datafile by each segment.
SELECT e.tablespace_name,
e.owner,
e.segment_name,
e.relative_fno,
f.file_name,
COUNT(*) extents,
SUM(e.blocks) blocks,
SUM(e.bytes) bytes -- this is just the number of blocks * blocksize
FROM dba_extents e,
dba_data_files f
WHERE ((e.owner = 'AR' AND e.segment_name = 'RA_CUST_TRX_LINE_GL_DIST_ALL')
OR (e.owner, e.segment_name) IN
(SELECT i.owner, i.index_name
FROM all_indexes i
WHERE i.table_owner = 'AR'
AND i.table_name = 'RA_CUST_TRX_LINE_GL_DIST_ALL')
) -- this can be replaced by different segment criteria
AND f.tablespace_name = e.tablespace_name
AND f.relative_fno = e.relative_fno
GROUP BY e.tablespace_name, e.owner, e.segment_name, e.relative_fno, f.file_name
ORDER BY e.tablespace_name, e.owner, e.segment_name, e.relative_fno, f.file_name;
TABLESPACE_NAME RELATIVE_FNO FILE_NAME
EXTENTS BLOCKS BYTES
------------------------------ ------------
------------------------------------------------------------ -------- ---------- ---------------
APPS_TS_TX_DATA 2 /scratch/u01/data/tx_data11.dbf
32 512 4,194,304
APPS_TS_TX_DATA 22 /scratch/u01/data/tx_data12.dbf
33 528 4,325,376
APPS_TS_TX_DATA 26 /scratch/u01/data/tx_data13.dbf
220 3,520 28,835,840
APPS_TS_TX_DATA 41 /scratch/u01/data/tx_data27.dbf
32 512 4,194,304
...
APPS_TS_TX_DATA 335 /scratch/u01/data/tx_data93.dbf
1 16 131,072
APPS_TS_TX_IDX 3 /scratch/u01/data/tx_idx11.dbf
57 912 7,471,104
APPS_TS_TX_IDX 24 /scratch/u01/data/tx_idx12.dbf
37 592 4,849,664
APPS_TS_TX_IDX 27 /scratch/u01/data/tx_idx13.dbf
37 592 4,849,664
...
Before and after reclaim, for those datafiles, identified above, where the most space is
likely to be saved, the amount of free space at the end of the datafile can be
determined along with the segment closest to the end of the datafile.
Clearly a certain percentage of space is required at the end of datafiles for growth, so
not all this space could be reclaimed.
COLUMN tablespace_name FORMAT A20 HEADING 'Tablespace Name'
COLUMN relative_fno FORMAT 99990 HEADING 'Rel|Fno'
COLUMN file_name FORMAT A60 HEADING 'File Name'
COLUMN segment_type FORMAT A10 HEADING 'Last Seg|Type'
COLUMN owner FORMAT A10 HEADING 'Last Seg|Owner'
COLUMN segment_name FORMAT A35 HEADING 'Last Segment|Name'
COLUMN maxext_blk FORMAT 999,999,990 HEADING 'Last Block|For Seg'
COLUMN max_blk FORMAT 999,999,990 HEADING 'Last Block'
COLUMN size_MB FORMAT 999,999,990 HEADING 'Size MB'
COLUMN pct_free_tot FORMAT 990.0 HEADING '%age Free|in Total'
COLUMN pct_free_end FORMAT 990.0 HEADING '%age Free|at end'
WITH
file_det AS
(SELECT f.tablespace_name,
f.relative_fno,
f.file_name,
ROUND(f.bytes/(1024*1024),0) Size_MB
FROM dba_data_files f
WHERE f.file_name IN
(...
'/slot/ems10175/oracle/db/apps_st/data/apps_ts_tx_idx_157.dbf',
'/slot/ems10175/oracle/db/apps_st/data/apps_ts_tx_idx_118.dbf',
'/slot/ems10175/oracle/db/apps_st/data/apps_ts_tx_idx_158.dbf',
'/slot/ems10175/oracle/db/apps_st/data/apps_ts_tx_idx_159.dbf',
'/slot/ems10175/oracle/db/apps_st/data/apps_ts_tx_idx_177.dbf',
'/slot/ems10175/oracle/db/apps_st/data/apps_ts_tx_idx_160.dbf',
...
'/slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_36.dbf',
'/slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_61.dbf',
'/slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_62.dbf',
'/slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_63.dbf',
'/slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_64.dbf',
'/slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_65.dbf',
'/slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_66.dbf',
'/slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_67.dbf',
'/slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_68.dbf',
'/slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_69.dbf',
'/slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_70.dbf',
'/slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_71.dbf',
'/slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_72.dbf',
'/slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_73.dbf',
'/slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_18.dbf',
...) -- this can be replaced by different datafile criteria
)
,max_extent AS
(SELECT e.tablespace_name,
e.relative_fno,
f.file_name,
MAX(e.block_id) maxext_blid,
MAX(e.block_id+e.blocks-1) maxext_blk
FROM dba_extents e,
file_det f
WHERE f.tablespace_name = e.tablespace_name -- must also specify table space as relative_fno
is only unique within tablespace
AND f.relative_fno = e.relative_fno
GROUP BY e.tablespace_name, e.relative_fno, f.file_name
)
,max_fs AS
(SELECT fs.tablespace_name,
fs.relative_fno,
f.file_name,
MAX(fs.block_id) maxfs_blid,
MAX(fs.block_id+fs.blocks-1) maxfs_blk,
SUM(fs.blocks) tot_free
FROM dba_free_space fs,
file_det f
WHERE f.tablespace_name = fs.tablespace_name -- must also specify table space as relative_fno
is only unique within tablespace
AND f.relative_fno = fs.relative_fno
GROUP BY fs.tablespace_name, fs.relative_fno, f.file_name)
SELECT f.tablespace_name,
f.relative_fno,
f.file_name,
e.segment_type,
e.owner,
e.segment_name,
me.maxext_blk,
GREATEST(NVL(mf.maxfs_blk,0), me.maxext_blk) max_blk,
f.size_MB,
ROUND(NVL(mf.tot_free,0)*100/GREATEST(NVL(mf.maxfs_blk,0), me.maxext_blk),1) pct_free_tot,
ROUND(((GREATEST(NVL(mf.maxfs_blk,0), me.maxext_blk) -
me.maxext_blk)*100)/GREATEST(NVL(mf.maxfs_blk,0), me.maxext_blk),1) pct_free_end
FROM dba_extents e,
file_det f,
max_extent me,
max_fs mf
WHERE e.block_id = me.maxext_blid
AND f.tablespace_name = e.tablespace_name -- must also specify table space as relative_fno is
only unique within tablespace
AND f.relative_fno = e.relative_fno
AND f.tablespace_name = me.tablespace_name
AND f.relative_fno = me.relative_fno
AND f.tablespace_name = mf.tablespace_name (+)
AND f.relative_fno = mf.relative_fno (+)
ORDER BY f.tablespace_name, f.relative_fno, f.file_name;
Example Output:
So for each of these datafiles we can see how far from the end the last extent for each
segment is and how much space can be saved by re-sizing.
But as segments are moved, this script will show more space becoming available and
indicate the next set of segments to be moved.
The following script gives shows the number of segments that need to be moved to
reduce the space needed by a single datafile.
The results are in descending order, with those segments closest to the end of the file
coming first.
Note that the "Pct After" column gives the percentage of space that occurs after that
segment. So to identify the space that could be save by moving that segment, look at
the next row down.
Also note that many segments have multiple contiguous blocks of extents. Here the
data is ordered by the last block (in the last extent for each segment). In many cases
the first extent (denoted by min block id) occurs much earlier in the data file.
COLUMN tablespace_name FORMAT A20 HEADING 'Tablespace Name'
COLUMN relative_fno FORMAT 99990 HEADING 'Rel|Fno'
COLUMN file_name FORMAT A60 HEADING 'File Name'
COLUMN owner FORMAT A10 HEADING 'Owner'
COLUMN segment_name FORMAT A35 HEADING 'Segment Name'
COLUMN Min_block FORMAT 999,999,990 HEADING 'First Block|For Seg'
COLUMN Max_block FORMAT 999,999,990 HEADING 'Last Block|For Seg'
COLUMN blocks FORMAT 999,999,990 HEADING 'Blocks used by Seg'
COLUMN pct_after FORMAT 990.0 HEADING 'Percent After|This Seg'
WITH segs AS
(SELECT e.tablespace_name,
e.relative_fno,
f.file_name,
e.owner,
e.segment_name,
MIN(e.block_id) Min_block,
MAX(e.blocks+e.block_id-1) Max_block,
SUM(e.blocks) blocks
FROM dba_extents e,
dba_data_files f
WHERE e.tablespace_name = 'A_TXN_DATA_02' -- this can be replaced by different datafile
criteria
AND e.relative_fno = 635 -- this can be replaced by different datafile criteria
AND f.tablespace_name = e.tablespace_name
AND f.relative_fno = e.relative_fno
GROUP BY e.tablespace_name, e.relative_fno, f.file_name, e.owner, e.segment_name
UNION ALL
SELECT fs.tablespace_name,
fs.relative_fno,
f.file_name,
'** Free **' owner,
'*** Free ***' segment_name,
fs.block_id Min_block,
fs.block_id+fs.blocks-1 Max_block,
fs.blocks blocks
FROM dba_free_space fs,
dba_data_files f
WHERE fs.tablespace_name = 'A_TXN_DATA_02' -- this can be replaced by different datafile
criteria
AND fs.relative_fno = 635 -- this can be replaced by different datafile criteria
AND f.tablespace_name = fs.tablespace_name
AND f.relative_fno = fs.relative_fno
)
,max_extent AS
(SELECT e.tablespace_name,
e.relative_fno,
MAX(e.block_id) maxext_blid,
MAX(e.block_id+e.blocks-1) maxext_blk
FROM dba_extents e
WHERE e.tablespace_name = 'A_TXN_DATA_02' -- this can be replaced by different datafile
criteria
AND e.relative_fno = 635 -- this can be replaced by different datafile criteria
GROUP BY e.tablespace_name, e.relative_fno)
,max_fs AS
(SELECT fs.tablespace_name,
fs.relative_fno,
MAX(fs.block_id) maxfs_blid,
MAX(fs.block_id+fs.blocks-1) maxfs_blk
FROM dba_free_space fs
WHERE fs.tablespace_name = 'A_TXN_DATA_02' -- this can be replaced by different datafile
criteria
AND fs.relative_fno = 635 -- this can be replaced by different datafile criteria
GROUP BY fs.tablespace_name, fs.relative_fno)
SELECT s.tablespace_name,
s.relative_fno,
s.file_name,
s.owner,
s.segment_name,
s.min_block,
s.max_block,
s.blocks,
ROUND(((GREATEST(me.maxext_blk, NVL(mf.maxfs_blk,0)) -
s.max_block)*100)/GREATEST(me.maxext_blk, NVL(mf.maxfs_blk,0)),1) pct_after
FROM segs s,
max_fs mf,
max_extent me
WHERE mf.tablespace_name (+) = s.tablespace_name
AND mf.relative_fno (+) = s.relative_fno
AND me.tablespace_name = s.tablespace_name
AND me.relative_fno = s.relative_fno
ORDER BY s.max_block DESC;
Example Outputs:
These are examples for different files, to illustrate some of the scenarios
In this case, in order to be able to re-size the data file and reclaim the feespace then 4 other
segments need to be moved (AP_PAYMENT_SCHEDULES_ALL, GL_INTERFACE, RA_INTERFACE_LINES_ALL and
PA_PROJECTS_ALL).
And for each data file used by the original segments (table and indexes) this list of segments
could be different.