Oracle 12c中不能直接用ALTER TABLE MODIFY PARTITION BY在线转换非分区表,因其对分区类型、虚拟列、NOT NULL约束等限制极多,易报ORA-14097等错;唯一稳定方案是DBMS_REDEFINITION,支持在线重定义但需严格满足主键、列类型、中间表建模及索引重建等条件。直接改分区键不能用
ALTER TABLE ... MODIFY PARTITION BY,尤其在生产环境——它对表结构、索引、默认值、虚拟列等限制极多,稍有不匹配就报 ORA-14097、ORA-14098 或直接失败。唯一稳得住的方案是 DBMS_REDEFINITION,全程原表可读可写,但必须绕过几个关键陷阱。
为什么 MODIFY PARTITION BY 在生产中大概率翻车
表面看语法简洁,实际踩坑密集:
• modify partition by range 不支持 list、hash、interval 分区类型;
• 分区键列不能是 generated always as 虚拟列(比如你的 total_amt 是计算列);
• 若原表某列有 not null 但没设 default,而新分区键允许空值,立刻触发 ora-14097;
• update indexes 子句对全局索引极其敏感:若原表有唯一约束依赖的全局索引,没显式列出或重建失败,报 ora-14098;
• 含 rowid 引用、物化视图日志、审计策略的表,该语法直接拒绝执行。
START_REDEF_TABLE 的三个致命参数怎么设
漏设或错设会导致数据丢失、分区信息清零、甚至同步卡死:
• col_mapping 必须显式列出所有列(含新增字段),不能用 * —— 否则丢掉虚拟列、默认值、NOT NULL 约束;
• options_flag 优先选 DBMS_REDEFINITION.CONS_USE_PK;若无主键,必须用 CONS_USE_ROWID,且后续必须手动跑 DBMS_REDEFINITION.CONS_VALIDATE 校验;
• 修改单个分区键逻辑(比如只调 CREATE_TIME 的分区范围),必须传 part_name => 'PAR_202405',否则报 ORA-14155(missing PARTITION keyword)。
中间表(int_table)建表时最容易被忽略的细节
中间表不是结构复制那么简单,差一个空格都可能失败:
• 字段类型必须完全一致:VARCHAR2(120) 和 VARCHAR2(120 CHAR) 被 Oracle 视为不同类型;
• 分区键列(如 CREATE_TIME)不能加 NOT NULL 约束,除非原表也强制非空,否则 START_REDEF_TABLE 报 ORA-14197;
• 中间表不能自己建索引/约束/触发器 —— 全部交由 COPY_TABLE_DEPENDENTS 同步,手动建反而冲突;
• 表空间必须和原表一致(除非你明确要迁移),否则重定义后索引、LOB 段可能跨表空间出问题。
FINISH_REDEF_TABLE 后全局索引为啥全变 UNUSABLE
这不是异常,是机制设计:
• 切换完成后,原表上的全局索引状态变成 UNUSABLE,查询会退化为全表扫描,但 DML 仍能执行;
• DBMS_STATS.GATHER_TABLE_STATS 不会修复它,必须手动重建:ALTER INDEX idx_name REBUILD;
• 验证命令是 SELECT index_name, status FROM dba_indexes WHERE table_name = 'YOUR_TABLE',状态必须是 VALID 才算真正生效;
• 如果忘了这步,业务慢得明显,但 DBA 往往先查 SQL 执行计划,最后才想到索引失效。
CAN_REDEF_TABLE 返回成功 ≠ 表真适合重定义;中间表建完得人工比对字段类型和长度;COPY_TABLE_DEPENDENTS 的 num_errors 输出必须为 0;同步前后的行数、分区边界、索引状态,一个都不能少。停机窗口可以没有,但检查清单不能跳。


















