Reorganizing a Table using DBMS_REDEFINITION
Posted at Thursday, July 03, 2008
There are many methods to reorganize/defragment a table in Oracle. If you want to avoid downtime, however, your choices are limited to using the 'SHRINK SPACE' feature and/or employing the DBMS_REDEFINITION PL/SQL package.
This post demonstrates the latter method. The DBMS_REDEFINITION package is most commonly used for redefining a table, however can be used to reorganize a table amongst other things. Here is a great article which covers the package in a little more detail: On-line Table Reorganization and Redefinition.
This example rebuilds the OE.ORDER_ITEMS table using DBMS_REDEFINITION.
Collect information about the table's segments prior to the reorg
Check whether the table in question violates any redefinition restrictions
Create the Intermediary Table - in my case, I use the exact same structure
Start the Redefinition Process
You will notice that Oracle creates a Materialized View in order to store the table changes, etc.
Sync up the Source table with the Intermediary table
Duplicate the table's dependent objects on the Intermediary table
Notice how, I create the foreign key constraints initially as disabled.
To simplify things, I use the following to duplicate any triggers to the Intermediary table
Disable any foreign key constraints on the Source table before finishing the table redefinition process
Finish the Redefinition
Drop the Intermediary Table
Rename any dependent objects
At this point, the table should be reorganized/defragmented. Optionally, compare the storage footprint post-redefintion
This post demonstrates the latter method. The DBMS_REDEFINITION package is most commonly used for redefining a table, however can be used to reorganize a table amongst other things. Here is a great article which covers the package in a little more detail: On-line Table Reorganization and Redefinition.
This example rebuilds the OE.ORDER_ITEMS table using DBMS_REDEFINITION.
Collect information about the table's segments prior to the reorg
ANALYZE TABLE oe.order_items COMPUTE STATISTICS;
SELECT avg_row_len, num_rows, chain_cnt
FROM dba_tables
WHERE table_name = 'ORDER_ITEMS' AND owner = 'OE';
SELECT ROUND (BYTES / 1024) kb
FROM dba_segments
WHERE owner = 'OE' AND segment_name = 'ORDER_ITEMS';
Check whether the table in question violates any redefinition restrictions
EXEC dbms_redefinition.can_redef_table('OE','ORDER_ITEMS', dbms_redefinition.cons_use_pk);Create the Intermediary Table - in my case, I use the exact same structure
CREATE TABLE OE.ORDER_ITEMS_REDEF
(
ORDER_ID NUMBER(12),
LINE_ITEM_ID NUMBER(3) NOT NULL,
PRODUCT_ID NUMBER(6) NOT NULL,
UNIT_PRICE NUMBER(8,2),
QUANTITY NUMBER(8)
) TABLESPACE USERS;
Start the Redefinition Process
EXEC dbms_redefinition.start_redef_table('OE', 'ORDER_ITEMS', 'ORDER_ITEMS_REDEF');You will notice that Oracle creates a Materialized View in order to store the table changes, etc.
SELECT COUNT (*)
FROM oe.mlog$_order_items;
Sync up the Source table with the Intermediary table
EXEC dbms_redefinition.sync_interim_table('OE', 'ORDER_ITEMS', 'ORDER_ITEMS_REDEF');Duplicate the table's dependent objects on the Intermediary table
CREATE INDEX oe.item_order_redef_ix ON oe.order_items_redef
(order_id) LOGGING TABLESPACE users;
CREATE INDEX oe.item_product_redef_ix ON oe.order_items_redef
(product_id) LOGGING TABLESPACE users;
CREATE UNIQUE INDEX oe.order_items_redef_pk ON oe.order_items_redef
(order_id, line_item_id) LOGGING TABLESPACE users;
CREATE UNIQUE INDEX oe.order_items_redef_uk ON oe.order_items_redef
(order_id, product_id) LOGGING TABLESPACE users;
ALTER TABLE oe.order_items_redef ADD (
CONSTRAINT order_items_redef_pk
PRIMARY KEY (order_id, line_item_id) USING INDEX TABLESPACE USERS);
ALTER TABLE oe.order_items_redef ADD (
CONSTRAINT order_items_redef_order_id_fk
FOREIGN KEY (order_id)
REFERENCES oe.orders (order_id)
ON DELETE CASCADE DISABLE NOVALIDATE);
ALTER TABLE oe.order_items_redef ADD (
CONSTRAINT order_items_rdef_product_id_fk
FOREIGN KEY (product_id)
REFERENCES oe.product_information (product_id) DISABLE NOVALIDATE);
GRANT SELECT ON oe.order_items_redef TO bi;
GRANT SELECT ON oe.order_items_redef TO pm;
Notice how, I create the foreign key constraints initially as disabled.
To simplify things, I use the following to duplicate any triggers to the Intermediary table
DECLARE
retval NUMBER(5);
BEGIN
dbms_redefinition.copy_table_dependents('OE', 'ORDER_ITEMS', 'ORDER_ITEMS_REDEF', copy_indexes=>0, copy_triggers=>TRUE, copy_constraints=>FALSE,copy_privileges=>FALSE, num_errors=>retval);
dbms_output.put_line(retval);
END;
/
Disable any foreign key constraints on the Source table before finishing the table redefinition process
ALTER TABLE oe.order_items DISABLE CONSTRAINT order_items_order_id_fk;
ALTER TABLE oe.order_items DISABLE CONSTRAINT order_items_product_id_fk;
Finish the Redefinition
EXEC dbms_redefinition.finish_redef_table('OE', 'ORDER_ITEMS', 'ORDER_ITEMS_REDEF');Drop the Intermediary Table
DROP TABLE oe.order_items_redef;
Rename any dependent objects
ALTER TABLE oe.order_items RENAME CONSTRAINT order_items_rdef_product_id_fk TO order_items_product_id_fk;
ALTER TABLE oe.order_items RENAME CONSTRAINT order_items_redef_order_id_fk TO order_items_order_id_fk;
ALTER TABLE oe.order_items RENAME CONSTRAINT order_items_redef_pk TO order_items_pk;
ALTER INDEX oe.item_order_redef_ix RENAME TO item_order_ix;
ALTER INDEX oe.item_product_redef_ix RENAME TO item_product_ix;
ALTER INDEX oe.order_items_redef_pk RENAME TO order_items_pk;
ALTER INDEX oe.order_items_redef_uk RENAME TO order_items_uk;
At this point, the table should be reorganized/defragmented. Optionally, compare the storage footprint post-redefintion
ANALYZE TABLE oe.order_items COMPUTE STATISTICS;
SELECT avg_row_len, num_rows, chain_cnt
FROM dba_tables
WHERE table_name = 'ORDER_ITEMS' AND owner = 'OE';
SELECT ROUND (BYTES / 1024) kb
FROM dba_segments
WHERE owner = 'OE' AND segment_name = 'ORDER_ITEMS';
Labels: Maintenance
