Вы находитесь на странице: 1из 24

Reducing the Oracle E-Business Suite Data Footprint (Doc ID 752322.

1)

Appendix B - SQL Script to identify the largest objects

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

nsactions and subledger journal entry lines.


Receivables AR RA_CUST_TRX_LINE_GL_DIST_ALL 699,088
230,828 468,259 0 0 222 EBS App Accounting records for
revenue, unearned revenue and unbille

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

nes. There is a one-to-many relationship between subledger

journal entry headers and subledger journal entry lines.


General Ledger GL GL_IMPORT_REFERENCES 422,195
194,569 227,626 0 0 103 EBS App Cross-references between
subledgers and Oracle General Ledge

r's journal entries


Payables AP AP_INVOICE_DISTRIBUTIONS_ALL 336,041
153,463 182,578 0 0 200 EBS App Invoice distribution line
information
E-Business Tax ZX ZX_LINES_DET_FACTORS 303,802
202,462 101,340 0 0 235 EBS App This table stores
transaction related attributes used for ta

x calculation and reporting. Each row stores a transaction l

ine of an event class.


Advanced Pricing QP QP_DEBUG_TEXT 274,735
113,876 160,859 0 0 661 EBS App This table stores the Debug
messages.
Incentive Compensation CN CN_COMMISSION_LINES_ALL 266,987
212,121 54,866 0 0 283 EBS App Stores all transactions
created as part of calculation.
Application Object Library APPLSYS FND_DOCUMENTS_TL 266,253
114,229 152,023 0 0 0 EBS App Translations for
FND_DOCUMENTS
Projects PA PA_EXPENDITURE_ITEMS_ALL 256,917
63,117 193,800 0 0 275 EBS App PA_EXPENDITURE_ITEMS_ALL
stores the smallest categorized exp

enditure units charged to projects and tasks.


Shipping Execution WSH WSH_ITM_PARTY_CONTACTS 238,791
217,970 20,821 0 0 665 EBS App ITM party contacts
General Ledger GL GL_JE_LINES 224,954
162,009 62,945 0 0 103 EBS App Journal entry lines
Application Object Library APPLSYS WF_ITEM_ATTRIBUTE_VALUES 214,731
87,887 122,360 4,470 14 0 EBS App Contains values for item
attributes
General Ledger GL GL_BALANCES 214,618
77,765 136,853 0 0 103 EBS App Account balances for both
detail and summary accounts
Order Capture ASO ASO_PRICE_ADJUSTMENTS 194,418
80,288 114,130 0 0 697 EBS App Table to Store the Price
Adjustments
Time and Labor Engine HXC HXC_RDB_PROCESS_DETAILS 192,334
107,849 84,485 0 0 809 EBS App Used for Retrieval
Dashboard Processing
Incentive Compensation CN CN_COMM_LINES_API_ALL 189,085
134,849 54,236 0 0 283 EBS App API for importing values
into CN_COMMISSION_LINES
Inventory INV MTL_SYSTEM_ITEMS_TL 155,847
50,366 105,481 0 0 401 EBS App Translations table table
holding item descriptions in multip

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

ple event classes.


Shipping Execution WSH WSH_EXCEPTIONS 148,134
60,868 87,266 0 0 665 EBS App Shipping Exceptions
Advanced Benefits BEN BEN_PERSON_ACTIONS 138,327
26,690 111,637 0 0 805 EBS App Identifies persons being
processed by benefits batch proces

Appendix C – SQL Script to identify table limitations

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 example is for table MTL_MATERIAL_TRANSACTIONS.

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

Appendix E – Scripts to Determine Freespace

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.

This example is for table MTL_MATERIAL_TRANSACTIONS.


SET SERVEROUTPUT ON

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);

DBMS_OUTPUT.PUT_LINE('Unformatted Blocks = '||l_unformatted_blocks);


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;
/

This will output something like the following:

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:

This example is for table MTL_MATERIAL_TRANSACTIONS.


SET SERVEROUTPUT ON

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_SPACE.SPACE_USAGE(segment_owner => r_index.owner,


segment_name => r_index.index_name,
segment_type => 'INDEX',
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);

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;
/

This will output something like the following:

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
....

Table (not ASSM)

For segments not using Automatic Segment Space Management (ASSM) then use the
following to identify the space available on a table:

This example is for table HZ_IMP_PARTIES_INT.


SET SERVEROUTPUT ON

DECLARE

l_free_blocks NUMBER;

BEGIN

DBMS_SPACE.FREE_BLOCKS(segment_owner => 'AR',


segment_name => 'HZ_IMP_PARTIES_INT',
segment_type => 'TABLE',
freelist_group_id => 0,
free_blks => l_free_blocks);

DBMS_OUTPUT.PUT_LINE('FREE_BLOCKS = '||l_free_blocks);

END;
/

This will output something like the following:

FREE_BLOCKS = 135581

Indexes (Not ASSM)

For segments not using Automatic Segment Space Management (ASSM) the following
can be used to identify the space available on indexes for a table:

This example is for table HZ_IMP_PARTIES_INT


SET SERVEROUTPUT ON

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_SPACE.FREE_BLOCKS(segment_owner => r_index.owner,


segment_name => r_index.index_name,
segment_type => 'INDEX',
freelist_group_id => 0,
free_blks => l_free_blocks);

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;
/

This will output something like the following:

Owner = AR
Index = HZ_IMP_PARTIES_INT_U1
FREE_BLOCKS = 11
...

Appendix F – Example Scripts to Reclaim Freespace

All of these example scripts use the table MTL_MATERIAL_TRANSACTIONS.

These scripts are provided for educational purposes only and not supported by Oracle
Support Services.

CREATE TABLE … AS SELECT


CONNECT APPS; -- This will prompt for password

-- Create the interim table - omit the PARALLEL modifier and hints to do it in serial.

CREATE TABLE inv.mtl_material_transactions_int PARALLEL


AS
SELECT /*+ parallel(i) full(i) */ i.*
FROM inv.mtl_material_transactions i;

DROP TABLE inv.mtl_material_transactions;

ALTER TABLE inv.mtl_material_transactions_int RENAME TO mtl_material_transactions;

ALTER TABLE inv.mtl_material_transactions NOPARALLEL; -- if the create was done in parallel.

-- 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.

CONNECT INV; -- This will prompt for password

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;

-- Create all the indexes - omit the PARALLEL modifier to do it in serial.


CREATE INDEX INV.MTL_MATERIAL_TRANSACTIONS_N1 ON INV.MTL_MATERIAL_TRANSACTIONS
(INVENTORY_ITEM_ID, ORGANIZATION_ID, TRANSACTION_DATE, COSTED_FLAG) PARALLEL;
CREATE INDEX INV.MTL_MATERIAL_TRANSACTIONS_N10 ON INV.MTL_MATERIAL_TRANSACTIONS (COSTED_FLAG,
TRANSACTION_GROUP_ID) PARALLEL;
CREATE INDEX INV.MTL_MATERIAL_TRANSACTIONS_N11 ON INV.MTL_MATERIAL_TRANSACTIONS
(PARENT_TRANSACTION_ID) PARALLEL;
... and all the other indexes.

-- if the indexes were created in parallel then revert to NOPARALLEL

ALTER INDEX INV.MTL_MATERIAL_TRANSACTIONS_N1 NOPARALLEL;


ALTER INDEX INV.MTL_MATERIAL_TRANSACTIONS_N10 NOPARALLEL;
ALTER INDEX INV.MTL_MATERIAL_TRANSACTIONS_N11 NOPARALLEL;
... and all the other indexes.

ALTER TABLE … MOVE


ALTER TABLE INV.MTL_MATERIAL_TRANSACTIONS MOVE TABLESPACE APPS_TS_TX_DATA PARALLEL; -- Where
APPS_TS_TX_DATA is the same tablespace that the table resides in.

ALTER TABLE INV.MTL_MATERIAL_TRANSACTIONS NOPARALLEL; -- if the MOVE was done in parallel.

-- It will be necessary to rebuild indexes afterwards

ALTER TABLE ... SHRINK SPACE


ALTER TABLE INV.MTL_MATERIAL_TRANSACTIONS ENABLE ROW MOVEMENT;
ALTER TABLE INV.MTL_MATERIAL_TRANSACTIONS SHRINK SPACE CASCADE;
ALTER TABLE INV.MTL_MATERIAL_TRANSACTIONS DISABLE ROW MOVEMENT;

COMPACT can be used as well as cascade (e.g. ALTER TABLE


INV.MTL_MATERIAL_TRANSACTIONS SHRINK SPACE COMPACT CASCADE) to run the
first phase. which compacts the table rows, de-fragmenting the space, but which does
not reset the high water mark of de-allocate the space. In this case the command must
be run again without the COMPACT keyword to reset the high water mark and de-
allocate the space, releasing it back to the tablespace.

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

DBMS_REDEFINITION.REDEF_TABLE(uname => 'INV',


tname => 'MTL_MATERIAL_TRANSACTIONS',
table_part_tablespace => 'APPS_TS_TX_DATA',
index_tablespace => 'APPS_TS_TX_DATA');
END;
/

Alternatively it can be done using multiple steps as follows :


SET SERVEROUTPUT ON

-- determine if the table can be redefined.


BEGIN
DBMS_REDEFINITION.CAN_REDEF_TABLE(uname => 'INV',
tname => 'MTL_MATERIAL_TRANSACTIONS',
options_flag => DBMS_REDEFINITION.CONS_USE_PK); -- This
specifies using the "by key" method.
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('Exception raised');
dbms_output.put_line(SQLCODE);
dbms_output.put_line(SQLERRM);
RAISE;
END;
/

-- 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
);

-- Initiate the redefinition


BEGIN
DBMS_REDEFINITION.START_REDEF_TABLE(uname => 'INV',
orig_table => 'MTL_MATERIAL_TRANSACTIONS',
int_table => 'MTL_MATERIAL_TRANSACTIONS_INT',
options_flag => DBMS_REDEFINITION.CONS_USE_PK); -- This
specifies using the "by key" method.
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('Exception raised');
dbms_output.put_line(SQLCODE);
dbms_output.put_line(SQLERRM);
RAISE;
END;
/

-- to copy indexes, triggers, constraints and privileges.


-- All cloned referential constraints involving the interim tables will be created disabled (they
will be automatically enabled after the redefinition)
-- and all triggers on interim tables will not fire till the redefinition is completed.
DECLARE
l_num_errors PLS_INTEGER;
BEGIN
DBMS_REDEFINITION.COPY_TABLE_DEPENDENTS(uname => 'INV',
orig_table => 'MTL_MATERIAL_TRANSACTIONS',
int_table => 'MTL_MATERIAL_TRANSACTIONS_INT',
copy_indexes => dbms_redefinition.cons_orig_params, --
copy indexes using physical params of the source
copy_triggers => TRUE, -- default
copy_constraints => TRUE, -- default
copy_privileges => TRUE, -- default
ignore_errors => FALSE, -- default
num_errors => l_num_errors,
copy_statistics => TRUE,
copy_mvlog => TRUE);

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;
/

-- to complete the redefinition


BEGIN
DBMS_REDEFINITION.FINISH_REDEF_TABLE(uname => 'INV',
orig_table => 'MTL_MATERIAL_TRANSACTIONS',
int_table => 'MTL_MATERIAL_TRANSACTIONS_INT',
dml_lock_timeout => NULL); -- default
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('Exception raised');
dbms_output.put_line(SQLCODE);
dbms_output.put_line(SQLERRM);
RAISE;
END;
/

-- Drop the interim table


DROP TABLE INV.MTL_MATERIAL_TRANSACTIONS_INT;

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.

-- if the indexes were altered in parallel then revert to NOPARALLEL


ALTER INDEX INV.MTL_MATERIAL_TRANSACTIONS_N1 NOPARALLEL;
ALTER INDEX INV.MTL_MATERIAL_TRANSACTIONS_N10 NOPARALLEL;
ALTER INDEX INV.MTL_MATERIAL_TRANSACTIONS_N11 NOPARALLEL;
... and all the other indexes.

Appendix G – Scripts to assist with datafile re-sizing

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 can be done using the following script :


-- the following table will give the datafiles that are used by a segment and its indexes

COLUMN tablespace_name FORMAT A30


COLUMN owner FORMAT A10
COLUMN segment_name FORMAT A35
COLUMN relative_fno FORMAT 99990
COLUMN file_name FORMAT A60
COLUMN extents FORMAT 999,990
COLUMN blocks FORMAT 9,999,990
COLUMN bytes FORMAT 99,999,999,990

-- 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
...

TABLESPACE_NAME OWNER SEGMENT_NAME RELATIVE_FNO


FILE_NAME EXTENTS BLOCKS BYTES
------------------------------ ---------- ----------------------------------- ------------
------------------------------------------------------------ -------- ---------- ---------------
APPS_TS_TX_DATA AR RA_CUST_TRX_LINE_GL_DIST_ALL 2
/scratch/u01/data/tx_data11.dbf 32 512 4,194,304
APPS_TS_TX_DATA AR RA_CUST_TRX_LINE_GL_DIST_ALL 22
/scratch/u01/data/tx_data12.dbf 33 528 4,325,376
APPS_TS_TX_DATA AR RA_CUST_TRX_LINE_GL_DIST_ALL 26
/scratch/u01/data/tx_data13.dbf 220 3,520 28,835,840
...
APPS_TS_TX_IDX AR RA_CUST_TRX_LINE_GL_DIST_N1 27
/scratch/u01/data/tx_idx13.dbf 20 320 2,621,440
APPS_TS_TX_IDX AR RA_CUST_TRX_LINE_GL_DIST_N1 33
/scratch/u01/data/tx_idx14.dbf 20 320 2,621,440
APPS_TS_TX_IDX AR RA_CUST_TRX_LINE_GL_DIST_N1 39
/scratch/u01/data/tx_idx15.dbf 20 320 2,621,440
...
APPS_TS_TX_IDX AR RA_CUST_TRX_LINE_GL_DIST_U1 269
/scratch/u01/data/tx_idx59.dbf 28 448 3,670,016
APPS_TS_TX_IDX AR RA_CUST_TRX_LINE_GL_DIST_U1 270
/scratch/u01/data/tx_idx60.dbf 28 448 3,670,016
APPS_TS_TX_IDX AR RA_CUST_TRX_LINE_GL_DIST_U1 271
/scratch/u01/data/tx_idx61.dbf 28 448 3,670,016
APPS_TS_TX_IDX AR RA_CUST_TRX_LINE_GL_DIST_U1 272
/scratch/u01/data/tx_idx62.dbf 27 432 3,538,944

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:

Tablespace Name Rel File Name Last Seg


Last Seg Last Segment Last Block Last Block Size MB %age Free
%age Free
Fno Type
Owner Name For Seg in Total
at End
-------------------- ----- ------------------------------------------------------------
---------- ---------- ----------------------------------- ------------ ------------ ------------
----------- -----------
...
APPS_TS_TX_IDX 538 /slot/ems10175/oracle/db/apps_st/data/apps_ts_tx_idx_157.dbf INDEX
AR RA_CUST_TRX_LINE_GL_DIST_N10 1,611,687 1,696,511 13,254 6.7
5.0
APPS_TS_TX_IDX 551 /slot/ems10175/oracle/db/apps_st/data/apps_ts_tx_idx_118.dbf INDEX
HXC HXC_RDB_PROCESS_DETAILS_N3 2,609,663 2,614,015 20,422 0.9
0.2
APPS_TS_TX_IDX 553 /slot/ems10175/oracle/db/apps_st/data/apps_ts_tx_idx_158.dbf INDEX
CE MISCE_STATEMENT_RECONS_N1 1,696,511 1,696,511 13,254 4.5
0.0
APPS_TS_TX_IDX 574 /slot/ems10175/oracle/db/apps_st/data/apps_ts_tx_idx_159.dbf INDEX
MISPER MISPER_LOC_PREF_N3 1,696,511 1,696,511 13,254 0.3
0.0
APPS_TS_TX_IDX 578 /slot/ems10175/oracle/db/apps_st/data/apps_ts_tx_idx_177.dbf INDEX
WSH WSH_EXCEPTIONS_N1 2,482,943 2,482,943 19,398 1.0
0.0
APPS_TS_TX_IDX 586 /slot/ems10175/oracle/db/apps_st/data/apps_ts_tx_idx_160.dbf INDEX
HXC HXC_RET_PA_LATEST_DETAILS_N5 1,696,511 1,696,511 13,254 4.3
0.0
...
A_TXN_DATA_02 622 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_36.dbf TABLE
AR HZ_RELATIONSHIPS 1,696,392 1,696,512 13,254 10.5
0.0
A_TXN_DATA_02 634 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_61.dbf TABLE
AP AP_PAYMENT_SCHEDULES_ALL 1,696,511 1,696,511 13,254 3.7
0.0
A_TXN_DATA_02 635 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_62.dbf TABLE
AP AP_PAYMENT_SCHEDULES_ALL 1,696,511 1,696,511 13,254 30.0
0.0
A_TXN_DATA_02 636 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_63.dbf TABLE
AP AP_PAYMENT_SCHEDULES_ALL 1,696,511 1,696,511 13,254 11.3
0.0
A_TXN_DATA_02 637 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_64.dbf TABLE
MISCN MISCN_COMM_LINES_API_ALL 1,696,511 1,696,511 13,254 7.6
0.0
A_TXN_DATA_02 638 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_65.dbf TABLE
AP AP_PAYMENT_SCHEDULES_ALL 1,696,511 1,696,511 13,254 3.1
0.0
A_TXN_DATA_02 639 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_66.dbf TABLE
AP AP_PAYMENT_SCHEDULES_ALL 1,696,511 1,696,511 13,254 4.1
0.0
A_TXN_DATA_02 640 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_67.dbf TABLE
AP AP_PAYMENT_SCHEDULES_ALL 1,696,511 1,696,511 13,254 2.0
0.0
A_TXN_DATA_02 641 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_68.dbf TABLE
AP AP_PAYMENT_SCHEDULES_ALL 1,696,511 1,696,511 13,254 5.3
0.0
A_TXN_DATA_02 642 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_69.dbf TABLE
AP AP_PAYMENT_SCHEDULES_ALL 1,696,511 1,696,511 13,254 3.6
0.0
A_TXN_DATA_02 643 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_70.dbf TABLE
AP AP_PAYMENT_SCHEDULES_ALL 1,696,511 1,696,511 13,254 2.4
0.0
A_TXN_DATA_02 644 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_71.dbf TABLE
AP AP_PAYMENT_SCHEDULES_ALL 1,696,511 1,696,511 13,254 1.9
0.0
A_TXN_DATA_02 645 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_72.dbf TABLE
INV MTL_ITEM_REVISIONS_TL 1,953,791 1,958,655 15,302 0.4
0.2
A_TXN_DATA_02 646 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_73.dbf TABLE
CN CN_COMM_LINES_API_ALL 2,089,471 2,089,727 16,326 0.1
0.0
A_TXN_DATA_02 681 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_18.dbf TABLE
AR HZ_CONTACT_POINTS 1,696,392 1,696,512 13,254 10.9
0.0
...

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.

Prior to moving any tables, it is not likely to be very much.

But as segments are moved, this script will show more space becoming available and
indicate the next set of segments to be moved.

In this case the first segments to be moved would include


RA_CUST_TRX_LINE_GL_DIST_N10, HXC_RDB_PROCESS_DETAILS_N3,
MISCE_STATEMENT_RECONS_N1, MISPER_LOC_PREF_N3, WSH_EXCEPTIONS_N1,
HXC_RET_PA_LATEST_DETAILS_N5, HZ_RELATIONSHIPS,
AP_PAYMENT_SCHEDULES_ALL, MISCN_COMM_LINES_API_ALL,
MTL_ITEM_REVISIONS_TL, CN_COMM_LINES_API_ALL and HZ_CONTACT_POINTS.

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

Tablespace Name Rel File Name Owner


Segment Name First Block Last Block Blocks used Percent After
Fno
For Seg For Seg This Seg
-------------------- ----- ------------------------------------------------------------
---------- ----------------------------------- ----------- ---------- ----------- -------------
A_TXN_DATA_02 635 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_62.dbf AP
AP_PAYMENT_SCHEDULES_ALL 1,694,080 1,696,511 2,432 0.0
A_TXN_DATA_02 635 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_62.dbf GL
GL_INTERFACE 1,693,312 1,694,079 640 0.0
A_TXN_DATA_02 635 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_62.dbf ** Free
** *** Free *** 1,693,440 1,693,567 128 0.0
A_TXN_DATA_02 635 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_62.dbf
RECEIVABLE RA_INTERFACE_LINES_ALL 1,693,184 1,693,311 128 0.0
A_TXN_DATA_02 635 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_62.dbf ** Free
** *** Free *** 1,692,664 1,693,183 520 0.0
A_TXN_DATA_02 635 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_62.dbf PA
PA_PROJECTS_ALL 1,692,568 1,692,663 96 0.1
A_TXN_DATA_02 635 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_62.dbf ** Free
** *** Free *** 1,178,664 1,692,567 513,904 0.1
A_TXN_DATA_02 635 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_62.dbf
RECEIVABLE RA_CUST_TRX_LINE_GL_DIST_ALL 135,296 1,178,663 1,043,368 30.1
A_TXN_DATA_02 635 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_62.dbf
RECEIVABLE RA_CUST_TRX_LINE_SALESREPS_ALL 16,512 135,295 118,784 92.0
A_TXN_DATA_02 635 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_62.dbf
RECEIVABLE RA_INTERFACE_LINES_B 8,320 16,511 8,192 99.0
A_TXN_DATA_02 635 /slot/ems10175/oracle/db/apps_st/data/a_txn_data_02_62.dbf
RECEIVABLE RA_CUSTOMER_TRX_LINES_ALL 128 8,319 8,192 99.5

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.