【问题标题】:Partition exchange column type or size mismatch (ORA-14097)分区交换列类型或大小不匹配 (ORA-14097)
【发布时间】:2017-01-16 20:16:57
【问题描述】:

我正在尝试对数据库进行交换分区,但出现以下错误:ORA-14097: ALTER TABLE EXCHANGE PARTITION 中的列类型或大小不匹配

执行此操作的脚本已经创建,并且它在 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 上,我遇到了上述错误。我做了一些研究,我看到人们谈论 INVISIBLE 列。好吧,我重新创建了 OLD_TABLE,所以我认为不会有不可见的列。

编辑:

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

谢谢你们!

【问题讨论】:

  • 难道你有一个列组来支持相关的统计数据?

标签: partitioning oracle12c


【解决方案1】:

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

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

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 有帮助。

【讨论】:

    【解决方案2】:

    另一个不妥协的可靠解决方案是使用“..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】:

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

      在此之后,分区交换工作。

      【讨论】:

        【解决方案4】:

        最明显的是 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 字符)。

        【讨论】:

          猜你喜欢
          • 2021-10-03
          • 2022-06-13
          • 2013-10-19
          • 2017-12-12
          • 2017-11-14
          • 2012-03-15
          • 1970-01-01
          • 1970-01-01
          • 2019-10-22
          相关资源
          最近更新 更多