分区交换列类型或大小不匹配(ORA-14097)

时间:2017-01-16 20:16:57

标签: partitioning oracle12c

我试图在数据库上进行交换分区,但我遇到以下错误: ORA-14097:ALTER TABLE EXCHANGE PARTITION中的列类型或大小不匹配 < / p>

执行此操作的脚本已经创建,并且在Oracle 11g数据库上按预期运行。一旦我更新到12c,我就遇到了这个问题。这就是我进行分区交换的方式:

-- 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;

(...)

现在,再一次,在Oracle 11g上,这是完美的。在Oracle 12c上,我得到了上面解释的错误。我做了一些研究,我看到人们谈论不可见的专栏。好吧,我已经重新创建了OLD_TABLE,所以我认为没有不可见的列。

修改

我已经意识到在Oracle 12c上,当我尝试更改表以创建新列时,会创建另一个不可见的列(名为SYS_NC00011 $)。这就是分区交换不起作用的原因。我现在的问题是为什么会发生这种情况,以及删除此列的最佳方式是什么?#34; ?已经尝试删除未使用的列但没有成功。

谢谢你们!

4 个答案:

答案 0 :(得分:1)

我们最近遇到了同样的错误。与您的情况类似,错误是由隐藏的列触发的(它甚至不是复活节;-)。在我们的例子中,隐藏列是由ALTER TABLE xxx DROP COLUMN yyy压缩表引起的。

在您的情况下,隐藏列很可能是由ALTER TABLE xxx ADD COLUMN yyy NULL创建的。正如文章DDL Optimization in Oracle Database 12c和此answer所解释的那样,添加NULL列会执行一些数据字典魔术,并添加一个隐藏列来跟踪是否已为每一行写入新列。< / p>

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) 

因此,要修复您的情况,请重新创建包含列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'
 );

或添加没有DEFAULT的列:

ALTER TABLE OLD_TABLE ADD partition_time TIMESTAMP(6) NULL;

或停用新功能(文档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) 

我还没有找到重建表来摆脱隐藏列的方法。 ALTER TABLE MOVE没有帮助,只有CREATE TABLE AS SELECT会这样做。

答案 1 :(得分:0)

最明显的一点是NEW_TABLE有一个PARTITION_TIME列,而OLD_TABLE没有。 要检查的其他事项,可能是一个问题

  • NEW_TABLE.ID为NUMBER(18,0),而OLD_TABLE.ID为NUMBER(18)
  • OLD_TABLE.MESSAGE是VARCHAR2(4000字节)。你应该检查你的 长度语义,因为如果它们被定义为CHAR,那么
    NEW_TABLE.message将是VARCHAR2(4000个字符)。

答案 2 :(得分:0)

另一种可妥协的解决方案是使用“ ..FOR EXCHANGE ..”子句创建/重建OLD_TABLE(未分区)。仅从Oracle 12.2版开始可用。

CREATE TABLE OLD_TABLE **FOR EXCHANGE** WITH TABLE NEW_TABLE;

从您的描述中不清楚OLD_TABLE是否为空或您的情况下是否有数据。如果有数据,则可以使用

填充其中的数据
INSERT INTO OLD_TABLE SELECT * FROM <old backup table>;

这避免了在“交换分区”无缝完成工作期间的ORA-14097(在某些情况下为ORA-00932)。 在引入与DEFAULT列属性相关的DDL优化之后,Oracle很快就会感觉到“交换分区”的问题,因此从12.2开始引入了CTAS操作的“ ..FOR EXCHANGE ..”版本。

答案 3 :(得分:0)

感谢 wolφi 突出显示隐藏的列并为我指明了正确的方向。
我用下面的查询确认了隐藏的列:

SELECT * FROM SYS.dba_tab_cols

然后我重新创建了我的临时表,包括系统生成的具有匹配类型的列名,并且根据 INTERNAL_COLUMN_ID 以相同的顺序。

分区交换仍然失败,因为新列显示为 USER_GENERATED='YES'

最后的修复是将列标记为未使用:

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$"  );

此后分区交换工作。