With Oracle 12.2 and higher versions, it is really simple to convert a non-partitioned table to a partitioned table: you can now use the "ALTER TABLE .... MODIFY" syntax.
Below I am showing how I used this feature for one of my tables.
First, find some basic info about the table as it is right now:
Output:
Alter the table. I am taking the opportunity to compress the table at the same time as I am getting it partitioned. I am also using the ONLINE keyword, allowing DML against the table during the operation:
Analyze the table:
Check the table properties again:
Output:
How about compression?
Check the DBA_TAB_PARTITIONS (I am only showing the first 5 rows for brevity):
Output:
How about existing indexes?
By default, existing non-unique indexes are also partitioned during the alteration of the table.
There are two indexes on my table, one unique index supporting the primary key constraint, and one non-unique index.
Before the table was altered, both were unpartitioned:
After the table was partitioned, the non-unique index was automatically partitioned, too:
Update 18.03.2020:
Another example using LIST partitioning:
Update 12.03.2021:
An example using subpartitions:
Note that UPDATE INDEXES converts IDX_1 and IDX_2 and convert them to LOCAL indexes, on the fly.
Source: Oracle Documentation
I also recommend to look at Oracle-base.com for further reading.
First, find some basic info about the table as it is right now:
SELECT TABLE_NAME, PARTITIONED,NUM_ROWS,COMPRESSION FROM DBA_TABLES WHERE TABLE_NAME='SEGMENT_SIZE_SAMPLES';
Output:
TABLE_NAME | PARTITIONED | NUM_ROWS | COMPRESSION |
---|---|---|---|
SEGMENT_SIZE_SAMPLES | NO | 948401 | DISABLED |
Alter the table. I am taking the opportunity to compress the table at the same time as I am getting it partitioned. I am also using the ONLINE keyword, allowing DML against the table during the operation:
ALTER TABLE SEGMENT_SIZE_SAMPLES MODIFY PARTITION BY RANGE (SAMPLE_DATE) INTERVAL ( NUMTOYMINTERVAL(1,'MONTH') ) ( PARTITION P_INIT VALUES LESS THAN (TO_DATE('2014-01','YYYY-MM') ) ) ROW STORE COMPRESS ADVANCED ONLINE;
Analyze the table:
exec DBMS_STATS.GATHER_TABLE_STATS (OwnName => 'DBDRIFT',TabName => 'SEGMENT_SIZE_SAMPLES');
Check the table properties again:
SELECT TABLE_NAME, PARTITIONED,NUM_ROWS,COMPRESSION FROM DBA_TABLES WHERE TABLE_NAME='SEGMENT_SIZE_SAMPLES';
Output:
TABLE_NAME | PARTITIONED | NUM_ROWS | COMPRESSION |
---|---|---|---|
SEGMENT_SIZE_SAMPLES | YES | 1000719 |
How about compression?
Check the DBA_TAB_PARTITIONS (I am only showing the first 5 rows for brevity):
SELECT TABLE_NAME, PARTITION_NAME,NUM_ROWS,COMPRESSION, COMPRESS_FOR FROM DBA_TAB_PARTITIONS WHERE TABLE_NAME='SEGMENT_SIZE_SAMPLES' FETCH FIRST 5 ROWS ONLY;
Output:
TABLE_NAME | PARTITION_NAME | NUM_ROWS | COMPRESSION | COMPRESS_FOR |
---|---|---|---|---|
SEGMENT_SIZE_SAMPLES | P_INIT | 0 | ENABLED | ADVANCED |
SEGMENT_SIZE_SAMPLES | SYS_P17074 | 19320 | ENABLED | ADVANCED |
SEGMENT_SIZE_SAMPLES | SYS_P17075 | 24955 | ENABLED | ADVANCED |
SEGMENT_SIZE_SAMPLES | SYS_P17076 | 24150 | ENABLED | ADVANCED |
SEGMENT_SIZE_SAMPLES | SYS_P17077 | 24934 | ENABLED | ADVANCED |
How about existing indexes?
By default, existing non-unique indexes are also partitioned during the alteration of the table.
There are two indexes on my table, one unique index supporting the primary key constraint, and one non-unique index.
Before the table was altered, both were unpartitioned:
SELECT INDEX_NAME,UNIQUENESS, PARTITIONED FROM DBA_INDEXES WHERE TABLE_NAME='SEGMENT_SIZE_SAMPLES';
INDEX_NAME | UNIQUENESS | PARTITIONED |
---|---|---|
SEGMENT_SIZE_SAMPLES_IDX1 | UNIQUE | NO |
SEGMENT_SIZE_SAMPLES_IDX2 | NONUNIQUE | NO |
After the table was partitioned, the non-unique index was automatically partitioned, too:
INDEX_NAME | UNIQUENESS | PARTITIONED |
---|---|---|
SEGMENT_SIZE_SAMPLES_IDX1 | UNIQUE | NO |
SEGMENT_SIZE_SAMPLES_IDX2 | NONUNIQUE | YES |
Update 18.03.2020:
Another example using LIST partitioning:
alter table MUSIC.ALBUM modify partition by list (GENRE) ( partition P_ROCK values (( 'ROCK')), partition P_POP values (( 'POP')), partition P_CLASSICAL values (( 'CLASSICAL')), partition P_MISC values (default) ) online;
Update 12.03.2021:
An example using subpartitions:
-- First, give the LOB the desired attributes, if you wish to change any of them: alter TABLE STOCKS MOVE LOB (DOC) STORE AS SECUREFILE( TABLESPACE DATA1 ENABLE STORAGE IN ROW CHUNK 8192 COMPRESS MEDIUM ); -- Alter the table alter TABLE STOCKS MODIFY PARTITION BY LIST (stockname) SUBPARTITION BY RANGE (LASTUPDATED) ( -- First partition is called a_name PARTITION a_name VALUES('a-abc') ( -- older values SUBPARTITION SP_a_name_1_older VALUES LESS THAN ( TO_DATE('2016-01', 'YYYY-MM')) TABLESPACE DATA1 LOB (DOC) STORE AS SECUREFILE ( TABLESPACE DATA1 ), -- 2016 subpartition SP_a_name_201601 VALUES LESS THAN (TO_DATE(' 2016-02-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS')) TABLESPACE DATA_2016 LOB (DOC) STORE AS SECUREFILE ( TABLESPACE DATA_2016 ), ...continue with one subpartition per month per year... ...contine with partitions b_name, c_name etc -- Final partition is called z_name PARTITION z_name VALUES(default) ( -- 2016 SUBPARTITION SP_sp_z_name_older VALUES LESS THAN ( TO_DATE('2016-01', 'YYYY-MM')) TABLESPACE DATA1, subpartition SP_sp_z_name_201601 VALUES LESS THAN (TO_DATE(' 2016-02-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS')) TABLESPACE DATA_2016 LOB (DOC) STORE AS SECUREFILE ( TABLESPACE DATA_2016 ), ...continue with one subpartition per month per year... LOB (DOC) STORE AS SECUREFILE ( TABLESPACE DATA_2022 ), subpartition SP_z_name_202212 VALUES LESS THAN (TO_DATE(' 2023-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS')) TABLESPACE DATA_2022 LOB (DOC) STORE AS SECUREFILE ( TABLESPACE DATA_2022 ), SUBPARTITION SP_z_name_DEFAULT VALUES LESS THAN (MAXVALUE) TABLESPACE DATA1 LOB (DOC) STORE AS SECUREFILE ( TABLESPACE DATA1) ) ) ONLINE UPDATE INDEXES( IDX_1 LOCAL, IDX_2 LOCAL );
Note that UPDATE INDEXES converts IDX_1 and IDX_2 and convert them to LOCAL indexes, on the fly.
Source: Oracle Documentation
I also recommend to look at Oracle-base.com for further reading.