I am trying to move data between two partitioned tables. Below is the sample script. I am working on Oracle 19c and using 12c feature of creating table for exchange. Please suggest what is missing.
How can i move partitions between two partitioned tables. This is DW env, where SRC should contain data upto 7 days and keep on moving to target table. If there is any better way to move data between two tables, pls suggest that as well. Both source and target tables resides in single schema but two different TS.
--Source table creation
DROP TABLE STG_SRC;
CREATE TABLE STG_SRC
(
STG_SRC_ID NUMBER GENERATED BY DEFAULT AS IDENTITY NOT NULL,
NM VARCHAR2(4000),
BATCH_DT_ID NUMBER,
CONSTRAINT PK_STG_SRC PRIMARY KEY (STG_SRC_ID)
);
ALTER TABLE STG_SRC MODIFY PARTITION BY LIST (BATCH_DT_ID) AUTOMATIC (PARTITION INITIAL_PARTITION VALUES (20240101)) ONLINE UPDATE INDEXES;
INSERT INTO STG_SRC (BATCH_DT_ID, NM) VALUES (20240201,'Robert');
INSERT INTO STG_SRC (BATCH_DT_ID, NM) VALUES (20240202,'Keith');
INSERT INTO STG_SRC (BATCH_DT_ID, NM) VALUES (20240203,'Mike');
INSERT INTO STG_SRC (BATCH_DT_ID, NM) VALUES (20240204,'Sean');
INSERT INTO STG_SRC (BATCH_DT_ID, NM) VALUES (20240205,'Alex');
COMMIT;
SELECT * FROM STG_SRC;
SELECT * FROM USER_TAB_PARTITIONS WHERE TABLE_NAME = 'STG_SRC';
--Target table creation
DROP TABLE STG_TGT
CREATE TABLE STG_TGT
(
STG_TGT_ID NUMBER GENERATED BY DEFAULT AS IDENTITY NOT NULL,
NM VARCHAR2(4000),
BATCH_DT_ID NUMBER,
CONSTRAINT PK_STG_TGT PRIMARY KEY (STG_TGT_ID)
);
ALTER TABLE STG_TGT MODIFY PARTITION BY LIST (BATCH_DT_ID) AUTOMATIC (PARTITION INITIAL_PARTITION VALUES (20240101)) ONLINE UPDATE INDEXES;
SELECT * FROM STG_TGT;
SELECT * FROM USER_TAB_PARTITIONS WHERE TABLE_NAME = 'STG_TGT';
--Temp table creation
DROP TABLE STG_SRC_TMP;
CREATE TABLE STG_SRC_TMP FOR EXCHANGE WITH TABLE STG_SRC;
--Exchange partition
ALTER TABLE STG_SRC EXCHANGE PARTITION FOR (20240201) WITH TABLE STG_SRC_TMP INCLUDING INDEXES WITHOUT VALIDATION UPDATE GLOBAL INDEXES;
SELECT * FROM STG_SRC WHERE BATCH_DT_ID = 20240201; -- 0 RECORDS
SELECT * FROM STG_SRC_TMP WHERE BATCH_DT_ID = 20240201; -- 1 RECORDS
ALTER TABLE STG_TGT EXCHANGE PARTITION FOR (20240201) WITH TABLE STG_SRC_TMP INCLUDING INDEXES WITHOUT VALIDATION UPDATE GLOBAL INDEXES;
ORA-14702: The partition number is invalid or out-of-range -- Error
Comments
The partition doesn't exist in STG_TGT, so you can't exchange into it!
You can overcome this by pre-creating the partitions in STG_TGT. One way to do this is to insert a row for the new partition and roll it back.
For example:
create table autolist ( c1 int )
partition by list ( c1 ) automatic (
partition p0 values ( 0 )
);
create table autolist_temp
for exchange with table autolist;
insert into autolist_temp values ( 42 );
alter table autolist
exchange partition for ( 42 )
with table autolist_temp;
--ORA-14702: The partition number is invalid or out-of-range
select partition_name, high_value
from user_tab_partitions
where table_name = 'AUTOLIST';
/*
PARTITION_ HIGH_VALUE
---------- ---------------------
P0 0
*/
insert into autolist values ( 42 );
rollback;
select partition_name, high_value
from user_tab_partitions
where table_name = 'AUTOLIST';
/*
PARTITION_ HIGH_VALUE
---------- ---------------------
P0 0
SYS_P1660 42
*/
alter table autolist
exchange partition for ( 42 )
with table autolist_temp;
--Table AUTOLIST altered.
Another option - if you're able to - is to reconsider the partition method. The database can create interval partitions when exchanging into a non-existent one.
So you could do this:
create table interval_range ( c1 int )
partition by range ( c1 ) interval ( 10 ) (
partition p0 values less than ( 0 )
);
create table interval_range_temp
for exchange with table interval_range;
insert into interval_range_temp values ( 42 );
select partition_name, high_value
from user_tab_partitions
where table_name = 'INTERVAL_RANGE';
/*
PARTITION_ HIGH_VALUE
---------- --------------------------------------------------------------------------------
P0 0
*/
alter table interval_range
exchange partition for ( 42 )
with table interval_range_temp;
--Table INTERVAL_RANGE altered.
select partition_name, high_value
from user_tab_partitions
where table_name = 'INTERVAL_RANGE';
/*
PARTITION_ HIGH_VALUE
---------- --------------------------------------------------------------------------------
P0 0
SYS_P1664 50
*/
As BATCH_DT_ID looks like a date in disguise, I would also consider making this column a DATE data type if you go down this route.