Reputation: 1214
I'm trying to do a exchange partition on a database and I'm having the following error: ORA-14097: column type or size mismatch in ALTER TABLE EXCHANGE PARTITION
The script that does this was already created and it was running as expected on an Oracle 11g database. As soon as I've updated to 12c I've got this problem. This is how I'm doing the partition exchange:
-- The new partitioned table.
CREATE TABLE NEW_TABLE
(
id NUMBER(18) NOT NULL,
message VARCHAR2(4000) NOT NULL,
details VARCHAR2(4000),
partition_time TIMESTAMP(6) DEFAULT to_timestamp('01-01-2016','dd-mm-yyyy HH24:MI') NULL
) NOCOMPRESS LOGGING
PARTITION BY RANGE (partition_time) INTERVAL (NUMTODSINTERVAL(1,'HOUR'))
(PARTITION initial VALUES LESS THAN (to_timestamp('01-01-2016','dd-mm- yyyy HH24:MI')));
-- The old table.
CREATE TABLE OLD_TABLE
(
id NUMBER(18,0) NOT NULL,
message VARCHAR2(4000 byte) NOT NULL,
details VARCHAR2(4000),
);
-- Add the column that does not exist on the old table (keep the same columns).
ALTER TABLE OLD_TABLE ADD partition_time TIMESTAMP(6) DEFAULT to_timestamp('01-01-2016','dd-mm-yyyy HH24:MI') NULL;
ALTER TABLE NEW_TABLE
EXCHANGE PARTITION INITIAL
WITH TABLE OLD_TABLE
WITHOUT VALIDATION;
(...)
Now, once again, on Oracle 11g this was working perfectly. On Oracle 12c I've got the error explained above. I've did some research and I've seen people talk about INVISIBLE columns. Well, I've recreated the OLD_TABLE so I think there will be no invisible columns.
EDIT:
I've realized that on Oracle 12c when I try to alter the table to create a new column another invisible column is created (named SYS_NC00011$). This is why the partition exchange is not working. My question now is why is this happening and what is the best way to "remove this column" ? Already tryied to drop unused columns with no success.
Thank you guys!
Upvotes: 3
Views: 18721
Reputation: 8534
Thanks to wolφi for highlighting the hidden columns and pointing me in the right direction.
I confirmed the hidden columns with the query below:
SELECT * FROM SYS.dba_tab_cols
I then recreated my staging table including the system generated column names with matching types and in the same order according to INTERNAL_COLUMN_ID.
The partition exchange still failed because the new columns were showing as USER_GENERATED='YES'
The final fix was to mark the columns as unused:
ALTER TABLE STAGING_TABLE
set unused ("SYS_C00006_16092719:09:49$"
,"SYS_C00007_16092719:10:34$"
,"SYS_C00008_16092719:06:48$"
,"SYS_C00009_16092719:07:00$"
,"SYS_C00010_16092719:07:10$"
,"SYS_C00011_16092719:08:15$"
,"SYS_C00012_16092719:08:59$" );
After this the partition exchange worked.
Upvotes: 1
Reputation: 1163
Another reilable solution without compromising anything would be to create/rebuild OLD_TABLE (non-partitioned) using "..FOR EXCHANGE.." clause. It's available only from Oracle version 12.2 onwards.
CREATE TABLE OLD_TABLE **FOR EXCHANGE** WITH TABLE NEW_TABLE;
It's not clear from your description if the OLD_TABLE is empty or has data in your case. If you have data, you can populate data in it using
INSERT INTO OLD_TABLE SELECT * FROM <old backup table>;
This avoids ORA-14097 (or ORA-00932 in some cases) during the 'exchange partition' get the job done seamlessly. Oracle could sense issues with "exchange partition" soon after introducing DDL optimisation related to DEFAULT column attribute and hence introduced "..FOR EXCHANGE.." version of CTAS operation from 12.2 onwards.
Upvotes: 2
Reputation: 8361
We ran recently into the same error. Similar to your case, the error was triggered by a hidden column (and it wasn't even easter ;-). In our case the hidden column was caused by a ALTER TABLE xxx DROP COLUMN yyy
of a compressed table.
In your case, it seems very likely that the hidden column is created by the ALTER TABLE xxx ADD COLUMN yyy NULL
. As the article DDL Optimization in Oracle Database 12c and this answer explains, adding a NULL
column does some data dictionary magic and adds a hidden column to track if the new column has been written to for each row.
CREATE TABLE old_table (
id NUMBER(18,0) NOT NULL,
message VARCHAR2(4000 BYTE) NOT NULL,
details VARCHAR2(4000)
);
ALTER TABLE old_table ADD partition_time TIMESTAMP(6)
DEFAULT to_timestamp('01-01-2016','dd-mm-yyyy HH24:MI') NULL;
SELECT * FROM user_tab_cols WHERE table_name='OLD_TABLE';
ID NUMBER
MESSAGE VARCHAR2
DETAILS VARCHAR2
SYS_NC00004$ RAW
PARTITION_TIME TIMESTAMP(6)
So, to fix your case, either recreate the table including the column partition_time
:
CREATE TABLE old_table (
id NUMBER(18,0) NOT NULL,
message VARCHAR2(4000 BYTE) NOT NULL,
details VARCHAR2(4000),
partition_time TIMESTAMP(6) DEFAULT DATE '2016-01-01'
);
or add the column without a DEFAULT
:
ALTER TABLE OLD_TABLE ADD partition_time TIMESTAMP(6) NULL;
or disable the new feature (Doc Id 2277937.1):
ALTER SESSION SET "_add_col_optim_enabled"=FALSE ;
ALTER TABLE old_table ADD partition_time TIMESTAMP(6)
DEFAULT to_timestamp('01-01-2016','dd-mm-yyyy HH24:MI') NULL;
SELECT * FROM user_tab_cols WHERE table_name='OLD_TABLE';
ID NUMBER
MESSAGE VARCHAR2
DETAILS VARCHAR2
PARTITION_TIME TIMESTAMP(6)
I haven't found a way yet to rebuild the table to get rid of the hidden column. ALTER TABLE MOVE
does not help, only CREATE TABLE AS SELECT
does.
Upvotes: 3
Reputation: 4416
The most obvious one is that NEW_TABLE has a PARTITION_TIME column, while OLD_TABLE does not. The other things to check, that might be an issue
Upvotes: 0