ORA-14097 是因 Oracle 在 sys.col$ 中逐字段字节级比对失败所致,只要 column_id、data_type、data_length、nullable、默认值、隐藏列或未使用列任一不一致即报错;CTAS 丢失元数据导致列序错位、类型语义丢失(如 VARCHAR2(50 CHAR) 变为 VARCHAR2(50)),必须用 DBMS_METADATA.GET_DDL 重建交换表并核对字典视图。

ORA-14097 不是“类型写错了”,而是 Oracle 在 sys.col$ 字典里逐字段做字节级比对,只要 column_id、data_type、data_length、nullable、默认值、隐藏列、未使用列中任一不一致,就立刻拒绝交换——且不告诉你哪一列出了问题。
为什么 CTAS 建的交换表必报 ORA-14097
用 CREATE TABLE AS SELECT * 建交换表,看似省事,实则丢掉所有元数据:列物理顺序(column_id)、NOT NULL 约束、默认值、隐藏列、未使用列。Oracle 交换时不看语义,只按 user_tab_columns.column_id 顺序逐字段比对。
- 源表定义是
(id, name, created_at),而 CTAS 写成SELECT name, id, created_at FROM ...→column_id错位,直接失败 -
DESC或DBMS_METADATA.GET_DDL输出看起来一样,但sys.col$中的default$、property(是否隐藏)已不同 - 哪怕源表只有
DEFAULT SYSDATE,交换表是DEFAULT NULL,也触发错误
怎么查出到底是哪一列不一致
别靠肉眼比 DDL,必须查字典视图,逐项核对:
- 查列顺序和
column_id:SELECT column_name, column_id FROM user_tab_columns WHERE table_name IN ('PART_TABLE', 'STAGING_TABLE') ORDER BY table_name, column_id - 查类型细节:
SELECT column_name, data_type, data_length, data_precision, nullable FROM user_tab_columns WHERE table_name IN ('PART_TABLE', 'STAGING_TABLE') ORDER BY table_name, column_id - 查隐藏列和虚拟列:
SELECT column_name, hidden_column, virtual_column FROM user_tab_cols WHERE table_name IN ('PART_TABLE', 'STAGING_TABLE') AND (hidden_column = 'YES' OR virtual_column = 'YES') - 查未使用列:
SELECT column_name FROM user_tab_cols WHERE table_name = 'XXX' AND unused_col_count > 0
修复必须重建交换表,不能修修补补
ALTER TABLE MODIFY 或 ADD CONSTRAINT 只能补部分问题(比如 NOT NULL),但无法修复 column_id 错位、隐藏列缺失、data_length 差异等底层字节级不一致。
- 正确做法:用
DBMS_METADATA.GET_DDL('TABLE', 'PART_TABLE')拿到原始 DDL,完整执行建表(注意:要保留换行和逗号位置,手写易错) - 两边都清理未使用列:
ALTER TABLE xxx DROP UNUSED COLUMNS(注意锁表) - 如有隐藏列(如压缩表自动生成的
SYS_NC*),交换表需用ALTER TABLE ... ADD COLUMN INVISIBLE补齐 - 禁用
INCLUDING INDEXES,改用EXCLUDING INDEXES避开ORA-14098;索引后续单独重建
VARCHAR2(50) 和 VARCHAR2(50 CHAR) 真的不一样
Oracle 内部记录的是实际字节长度:VARCHAR2(50 CHAR) 在 UTF8 下可能存为 150 字节,data_length 就是 150;而 VARCHAR2(50) 默认是 BYTE,data_length 是 50。二者在 sys.col$ 中就是两条不同的记录。
- 类似陷阱还有:
NUMBERvsNUMBER(10,0)、DATEvsTIMESTAMP(6)、CHAR(10)vsVARCHAR2(10) - CTAS 不继承字符语义:
VARCHAR2(100 CHAR)的源表,CTAS 出来大概率是VARCHAR2(100)(即 BYTE),直接失败 - 查
data_length必须完全相等——差一个字节都不行
最常被忽略的是那些“看不见”的东西:未使用列残留、隐式添加的隐藏列、默认值差异。这些不会出现在 DESC 或简单 DDL 里,但 Oracle 交换时全都要校验。一旦出错,它不会提示“第3列 data_length 不同”,只会甩你一个 ORA-14097 然后退出。


















