How To Partition Existing Table Using DBMS_REDEFINITION
GOAL
The purpose of this document is to provide step by step instructions on how to convert unpartitioned table to partitioned one using dbms_redefinition package.
SOLUTION
1) Create un-partitioned table called: unpar_table:
SQL> CREATE TABLE unpar_table (
id NUMBER(10),
create_date DATE,
name VARCHAR2(100)
);
id NUMBER(10),
create_date DATE,
name VARCHAR2(100)
);
2) Apply some constraints to the table:
SQL> ALTER TABLE unpar_table ADD (
CONSTRAINT unpar_table_pk PRIMARY KEY (id)
);
SQL> CREATE INDEX create_date_ind ON unpar_table(create_date);
CONSTRAINT unpar_table_pk PRIMARY KEY (id)
);
SQL> CREATE INDEX create_date_ind ON unpar_table(create_date);
3) Gather statistics on the table:
SQL> EXEC DBMS_STATS.gather_table_stats(USER, 'unpar_table', cascade => TRUE);
4) Create a Partitioned Interim Table:
SQL> CREATE TABLE par_table (
id NUMBER(10),
create_date DATE,
name VARCHAR2(100)
)
PARTITION BY RANGE (create_date)
(PARTITION unpar_table_2005 VALUES LESS THAN (TO_DATE('01/01/2005', 'DD/MM/YYYY')),
PARTITION unpar_table_2006 VALUES LESS THAN (TO_DATE('01/01/2006', 'DD/MM/YYYY')),
PARTITION unpar_table_2007 VALUES LESS THAN (MAXVALUE));
id NUMBER(10),
create_date DATE,
name VARCHAR2(100)
)
PARTITION BY RANGE (create_date)
(PARTITION unpar_table_2005 VALUES LESS THAN (TO_DATE('01/01/2005', 'DD/MM/YYYY')),
PARTITION unpar_table_2006 VALUES LESS THAN (TO_DATE('01/01/2006', 'DD/MM/YYYY')),
PARTITION unpar_table_2007 VALUES LESS THAN (MAXVALUE));
5) Start the Redefinition Process:
a. Check the redefinition is possible using the following command:
SQL> EXEC Dbms_Redefinition.can_redef_table(USER, 'unpar_table');
b. If no errors are reported, start the redefintion using the following command:
SQL> BEGIN
DBMS_REDEFINITION.start_redef_table(
uname => USER,
orig_table => 'unpar_table',
int_table => 'par_table');
END;
/
DBMS_REDEFINITION.start_redef_table(
uname => USER,
orig_table => 'unpar_table',
int_table => 'par_table');
END;
/
Note: This operation can take quite some time to complete.
c. Optionally synchronize new table with interim name before index creation:
SQL> BEGIN
dbms_redefinition.sync_interim_table(
uname => USER,
orig_table => 'unpar_table',
int_table => 'par_table');
END;
/
dbms_redefinition.sync_interim_table(
uname => USER,
orig_table => 'unpar_table',
int_table => 'par_table');
END;
/
d. Create Constraints and Indexes:
SQL> ALTER TABLE par_table ADD (
CONSTRAINT unpar_table_pk2 PRIMARY KEY (id)
);
SQL> CREATE INDEX create_date_ind2 ON par_table(create_date);
CONSTRAINT unpar_table_pk2 PRIMARY KEY (id)
);
SQL> CREATE INDEX create_date_ind2 ON par_table(create_date);
e. Gather statistics on the new table:
SQL> EXEC DBMS_STATS.gather_table_stats(USER, 'par_table', cascade => TRUE);
f. Complete the Redefinition Process:
SQL> BEGIN
dbms_redefinition.finish_redef_table(
uname => USER,
orig_table => 'unpar_table',
int_table => 'par_table');
END;
/
dbms_redefinition.finish_redef_table(
uname => USER,
orig_table => 'unpar_table',
int_table => 'par_table');
END;
/
At this point the interim table has become the "real" table and their names have been switched in the name dictionary.
g. Remove original table which now has the name of the interim table:
g. Remove original table which now has the name of the interim table:
SQL> DROP TABLE par_table;
h. Rename all the constraints and indexes to match the original names:
ALTER TABLE unpar_table RENAME CONSTRAINT unpar_table_pk2 TO unpar_table_pk;
ALTER INDEX create_date_ind2 RENAME TO create_date_ind;
ALTER INDEX create_date_ind2 RENAME TO create_date_ind;
i. Check whether partitioning is successful or not:
SQL> SELECT partitioned
FROM user_tables
WHERE table_name = 'unpar_table';
PAR
---
YES
1 row selected.
SQL> SELECT partition_name
FROM user_tab_partitions
WHERE table_name = 'unpar_table';
PARTITION_NAME
------------------------------
unpar_table_2005
unpar_table_2006
unpar_table_2007
3 rows selected.
FROM user_tables
WHERE table_name = 'unpar_table';
PAR
---
YES
1 row selected.
SQL> SELECT partition_name
FROM user_tab_partitions
WHERE table_name = 'unpar_table';
PARTITION_NAME
------------------------------
unpar_table_2005
unpar_table_2006
unpar_table_2007
3 rows selected.
Restrictions for Online Redefinition of Tables in 9i
- There must be enough space to hold two copies of the table.
- Primary key columns cannot be modified.
- Tables must have primary keys.
- Redefinition must be done within the same schema.
- New columns added cannot be made NOT NULL until after the redefinition operation.
- Tables cannot contain LONGs, BFILEs or User Defined Types.
- Clustered tables cannot be redefined.
- Tables in the SYS or SYSTEM schema cannot be redefined.
- Tables with materialized view logs or materialized views defined on them cannot be redefined.
- Horizontal sub setting of data cannot be performed during the redefinition.
Restrictions for Online Redefinition of Tables in 11.2
- If the table is to be redefined using primary key or pseudo-primary keys (unique keys or constraints with all component columns having not null constraints), then the post-redefinition table must have the same primary key or pseudo-primary key columns. If the table is to be redefined using rowids, then the table must not be an index-organized table.
- After redefining a table that has a materialized view log, the subsequent refresh of any dependent materialized view must be a complete refresh.
- Tables that are replicated in an n-way master configuration can be redefined, but horizontal subsetting (subset of rows in the table), vertical subsetting (subset of columns in the table), and column transformations are not allowed.
- The overflow table of an index-organized table cannot be redefined online independently.
- Tables with fine-grained access control (row-level security) cannot be redefined online.
- Tables for which Flashback Data Archive is enabled cannot be redefined online. You cannot enable Flashback Data Archive for the interim table.
- Tables with
BFILE
columns cannot be redefined online. - Tables with
LONG
columns can be redefined online, but those columns must be converted toCLOBS
. Also,LONG RAW
columns must be converted toBLOBS
. Tables withLOB
columns are acceptable. - On a system with sufficient resources for parallel execution, and in the case where the interim table is not partitioned, redefinition of a
LONG
column to aLOB
column can be executed in parallel, provided that:
- The segment used to store the
LOB
column in the interim table belongs to a locally managed tablespace with Automatic Segment Space Management (ASSM) enabled. - There is a simple mapping from one
LONG
column to oneLOB
column, and the interim table has only oneLOB
column.
- The segment used to store the
- Tables in the
SYS
andSYSTEM
schema cannot be redefined online. - Temporary tables cannot be redefined.
- A subset of rows in the table cannot be redefined.
- Only simple deterministic expressions, sequences, and
SYSDATE
can be used when mapping the columns in the interim table to those of the original table. For example, subqueries are not allowed. - If new columns are being added as part of the redefinition and there are no column mappings for these columns, then they must not be declared
NOT
NULL
until the redefinition is complete. - There cannot be any referential constraints between the table being redefined and the interim table.
- Table redefinition cannot be done
NOLOGGING
. - For materialized view logs and queue tables, online redefinition is restricted to changes in physical properties. No horizontal or vertical subsetting is permitted, nor are any column transformations. The only valid value for the column mapping string is
NULL
. - You can convert a
VARRAY
to a nested table with theCAST
operator in the column mapping. However, you cannot convert a nested table to aVARRAY
.
========================================================================
Oracle Database - Enterprise Edition - Version 11.2.0.2.0 and later
Information in this document applies to any platform.
-- Initial setup of table to be partitioned.
-- Insert 1000 rows into the master table while the DBMS_REDEF is active.
-- This will use the mview log created by the DBMS_REDEFINITION.start_redef_table.
-- Check the MLOG$_ table to confirm these online updates have been recorded.
-- Run the dbms_redefinition.sync_interim_table to populate the new table structure (implements a MVIEW FAST REFRESH)
-- with the online updates. This will purge the mview log of each record applied.
-- This can be run many times and should be, before we do the Finish_REDEF_TABLE.
-- Finish_redef_table swaps the table names so the interim table becomes the original table name.
-- After completing this step, the original table is redefined with the attributes and data of the interim table.
-- The original table is locked briefly during this procedure.
-- Note, both tables will now be synchronised.
-- Dictionary views to confirm the change of strructure of our original table "UNPAR_TABLE".
-- At this point the Interim table can be dropped.
Information in this document applies to any platform.
PURPOSE
This Note has been written to provide some volume details and timings for the process
to convert a normal table to a partition table using the DBMS_REDEFINITION package.
The note will also include details of objects used by the process (Fast Refresh) and how
these become populated during the procedure.
The procedure is broken down into the following sections:
1. Create the Partition Table structure required, known as the Interim table.
2. Execute DBMS_REDEFINITION.can_redef_table...
3. Execute DBMS_REDEFINITION.start_redef_table...
4. Execute DBMS_REDEFINITION.sync_interim_table...
5. Execute DBMS_REDEFINITION.finish_redef_table...
6. Drop the interim table.
to convert a normal table to a partition table using the DBMS_REDEFINITION package.
The note will also include details of objects used by the process (Fast Refresh) and how
these become populated during the procedure.
The procedure is broken down into the following sections:
1. Create the Partition Table structure required, known as the Interim table.
2. Execute DBMS_REDEFINITION.can_redef_table...
3. Execute DBMS_REDEFINITION.start_redef_table...
4. Execute DBMS_REDEFINITION.sync_interim_table...
5. Execute DBMS_REDEFINITION.finish_redef_table...
6. Drop the interim table.
DETAILS
Worked example under 11.2.0.3
-- Initial setup of table to be partitioned.
CREATE TABLE unpar_table (
a NUMBER, y number,
name VARCHAR2(100), date_used date);
alter table unpar_table ADD (CONSTRAINT unpar_table_pk PRIMARY KEY (a,y));
-- load table with 1,000,000 rows
begin
for i in 1 .. 1000
loop
for j in 1 .. 1000
loop
insert into unpar_table values ( i, j, dbms_random.random, sysdate-j );
end loop;
end loop;
end;
/
commit;
PL/SQL procedure successfully completed.
Elapsed: 00:01:56.90
Commit complete.
SQL> EXEC DBMS_STATS.gather_table_stats(user, 'unpar_table', cascade => TRUE);
SELECT num_rows FROM user_tables WHERE table_name = 'UNPAR_TABLE';
NUM_ROWS
----------
1000000
Elapsed: 00:00:00.01
SQL> CREATE TABLE par_table (
a NUMBER, y number,
name VARCHAR2(100),date_used DATE)
PARTITION BY RANGE (date_used)
(PARTITION unpar_table_12 VALUES LESS THAN (TO_DATE('10/07/2012', 'DD/MM/YYYY')),
PARTITION unpar_table_15 VALUES LESS THAN (TO_DATE('15/07/2012', 'DD/MM/YYYY')),
PARTITION unpar_table_MX VALUES LESS THAN (MAXVALUE));
SQL> EXEC Dbms_Redefinition.can_redef_table(USER, 'unpar_table');
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.07
-- This procedure (DBMS_REDEFINITION.start_redef_table) creates a materialized view based on a CTAS, as we can see below with
-- the PREBUILT container table.
SQL> BEGIN
DBMS_REDEFINITION.start_redef_table(
uname => USER,
orig_table => 'unpar_table',
int_table => 'par_table');
END;
/
PL/SQL procedure successfully completed.
Elapsed: 00:00:06.69
select mview_name,container_name, build_mode from user_mviews;
MVIEW_NAME CONTAINER_NAME BUILD_MOD
------------------------------ ------------------------------ ---------
PAR_TABLE PAR_TABLE PREBUILT
Elapsed: 00:00:00.13
a NUMBER, y number,
name VARCHAR2(100), date_used date);
alter table unpar_table ADD (CONSTRAINT unpar_table_pk PRIMARY KEY (a,y));
-- load table with 1,000,000 rows
begin
for i in 1 .. 1000
loop
for j in 1 .. 1000
loop
insert into unpar_table values ( i, j, dbms_random.random, sysdate-j );
end loop;
end loop;
end;
/
commit;
PL/SQL procedure successfully completed.
Elapsed: 00:01:56.90
Commit complete.
SQL> EXEC DBMS_STATS.gather_table_stats(user, 'unpar_table', cascade => TRUE);
SELECT num_rows FROM user_tables WHERE table_name = 'UNPAR_TABLE';
NUM_ROWS
----------
1000000
Elapsed: 00:00:00.01
SQL> CREATE TABLE par_table (
a NUMBER, y number,
name VARCHAR2(100),date_used DATE)
PARTITION BY RANGE (date_used)
(PARTITION unpar_table_12 VALUES LESS THAN (TO_DATE('10/07/2012', 'DD/MM/YYYY')),
PARTITION unpar_table_15 VALUES LESS THAN (TO_DATE('15/07/2012', 'DD/MM/YYYY')),
PARTITION unpar_table_MX VALUES LESS THAN (MAXVALUE));
SQL> EXEC Dbms_Redefinition.can_redef_table(USER, 'unpar_table');
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.07
-- This procedure (DBMS_REDEFINITION.start_redef_table) creates a materialized view based on a CTAS, as we can see below with
-- the PREBUILT container table.
SQL> BEGIN
DBMS_REDEFINITION.start_redef_table(
uname => USER,
orig_table => 'unpar_table',
int_table => 'par_table');
END;
/
PL/SQL procedure successfully completed.
Elapsed: 00:00:06.69
select mview_name,container_name, build_mode from user_mviews;
MVIEW_NAME CONTAINER_NAME BUILD_MOD
------------------------------ ------------------------------ ---------
PAR_TABLE PAR_TABLE PREBUILT
Elapsed: 00:00:00.13
-- Insert 1000 rows into the master table while the DBMS_REDEF is active.
-- This will use the mview log created by the DBMS_REDEFINITION.start_redef_table.
-- Check the MLOG$_ table to confirm these online updates have been recorded.
SQL> begin
for i in 1001 .. 1010
loop
for j in 1001 .. 1100
loop
insert into unpar_table values ( i, j, dbms_random.random, sysdate-j );
end loop;
end loop;
end;
/
commit;
Elapsed: 00:00:00.24
SQL> select count(*) from MLOG$_UNPAR_TABLE;
COUNT(*)
----------
1000
for i in 1001 .. 1010
loop
for j in 1001 .. 1100
loop
insert into unpar_table values ( i, j, dbms_random.random, sysdate-j );
end loop;
end loop;
end;
/
commit;
Elapsed: 00:00:00.24
SQL> select count(*) from MLOG$_UNPAR_TABLE;
COUNT(*)
----------
1000
-- Run the dbms_redefinition.sync_interim_table to populate the new table structure (implements a MVIEW FAST REFRESH)
-- with the online updates. This will purge the mview log of each record applied.
-- This can be run many times and should be, before we do the Finish_REDEF_TABLE.
SQL> BEGIN
dbms_redefinition.sync_interim_table(
uname => USER,
orig_table => 'unpar_table',
int_table => 'par_table');
END;
PL/SQL procedure successfully completed.
Elapsed: 00:00:02.01
ALTER TABLE par_table ADD (CONSTRAINT par_table_pk2 PRIMARY KEY (a,y));
EXEC DBMS_STATS.gather_table_stats(USER, 'par_table', cascade => TRUE);
dbms_redefinition.sync_interim_table(
uname => USER,
orig_table => 'unpar_table',
int_table => 'par_table');
END;
PL/SQL procedure successfully completed.
Elapsed: 00:00:02.01
ALTER TABLE par_table ADD (CONSTRAINT par_table_pk2 PRIMARY KEY (a,y));
EXEC DBMS_STATS.gather_table_stats(USER, 'par_table', cascade => TRUE);
-- Finish_redef_table swaps the table names so the interim table becomes the original table name.
-- After completing this step, the original table is redefined with the attributes and data of the interim table.
-- The original table is locked briefly during this procedure.
BEGIN
dbms_redefinition.finish_redef_table(
uname => USER,
orig_table => 'unpar_table',
int_table => 'par_table');
END;
/
dbms_redefinition.finish_redef_table(
uname => USER,
orig_table => 'unpar_table',
int_table => 'par_table');
END;
/
-- Note, both tables will now be synchronised.
select count(*) from par_table ;
COUNT(*)
----------
1001000
select count(*) from unpar_table ;
COUNT(*)
----------
1001000
COUNT(*)
----------
1001000
select count(*) from unpar_table ;
COUNT(*)
----------
1001000
-- Dictionary views to confirm the change of strructure of our original table "UNPAR_TABLE".
SELECT partitioned FROM user_tables WHERE table_name = 'UNPAR_TABLE';
PAR
---
YES
SELECT partition_name, num_rows FROM user_tab_partitions WHERE table_name = 'UNPAR_TABLE';
PARTITION_NAME NUM_ROWS
------------------------------ ----------
UNPAR_TABLE_12 980000
UNPAR_TABLE_15 5000
UNPAR_TABLE_MX 16000
PAR
---
YES
SELECT partition_name, num_rows FROM user_tab_partitions WHERE table_name = 'UNPAR_TABLE';
PARTITION_NAME NUM_ROWS
------------------------------ ----------
UNPAR_TABLE_12 980000
UNPAR_TABLE_15 5000
UNPAR_TABLE_MX 16000
-- At this point the Interim table can be dropped.
drop TABLE par_table cascade constraints;
Note for previous versions.
For tests done under 11.1.0.7 the timings for the sync_interim_table where the interim table is large (1,000,000 in our case) the timings for this are considerably longer. This was not reviewed in depth, but initial investigation looks like the mview refresh for DBMS_REDEFINITION has been updated during the MERGE cycle.
example:
11.1.0.7
SYNC of 1000 rows -- Elapsed: 00:01:36.30 (1.5 minutes)
=====================================================================
For tests done under 11.1.0.7 the timings for the sync_interim_table where the interim table is large (1,000,000 in our case) the timings for this are considerably longer. This was not reviewed in depth, but initial investigation looks like the mview refresh for DBMS_REDEFINITION has been updated during the MERGE cycle.
example:
11.1.0.7
SYNC of 1000 rows -- Elapsed: 00:01:36.30 (1.5 minutes)
=====================================================================
Oracle dbms_redefinition |
|
Comments
Post a Comment
Oracle DBA Information