引起ORA-12991 问题可能的第4个原因
一、问题产生背景
进行指定表中某个字段的数据类型变更时,由于该字段中存在历史数据,因此需要:
1)新建一个临时字段并将历史数据进行赋值;
2)将原始字段删除;
3)临时字段更名为原始字段名。
执行第2)步时,Oracle报错:ORA-12991: column is referenced in a multi-column constraint(引用的列处于多列约束条件)。
二、问题调查和解决
1. check被删除的列所在的表是否存在该列的外键约束:如果有,则删除对应的列约束;如果没有,继续下一步查询。
SQL:
SELECT a.table_name,a.column_name,a.constraint_name,b.constraint_type
FROM user_cons_columns a,user_constraints b
WHERE a.table_name='target_table_name' AND a.column_name='target_column'
AND b.constraint_type='R' AND a.constraint_name=b.constraint_name;
2. 扩大约束限制查询:如果有,则删除对应的列约束;如果没有,继续下一步查询。
SQL:
SELECT a.table_name,a.column_name,a.constraint_name,b.constraint_type
FROM user_cons_columns a,user_constraints b
WHERE a.table_name='target_table_name' AND a.column_name='target_column'
AND a.constraint_name=b.constraint_name;
3. check被删除的列是否存在索引:若存在则删除;如果没有,继续下一步查询。
4. 当该列既无约束,又无索引时,需要检查Oracle 的补充日志(Supplemental Logging)中是否存在该表的记录:
SQL:
select * from user_log_groups where table_name = 'target_table_name';
如果查询结果不为空时,获取LOG_GROUP_NAME后,进行删除操作:
SQL:
alter table target_table_name drop supplemental log group LOG_GROUP_NAME;
5.当按照上述步骤检查和执行完后,再重新执行列删除即可成功。
ps:本人遇到的情况正是第4个原因导致的。经分析,删除前进行了大量数据的Update操作。测试环境均无该问题,只有生产环境出现,生产环境是Oracle集群,且启用了补充日志。
三、相关资料
1. Oracle 表中字段的约束类型释义:
2. Oracle 补充日志:
只要是针对 UPDATE 命令的,是对重做日志记录中变更矢量块的补充信息,增加了变更矢量记载的记录量。
日志挖掘器(LogMiner)、闪回事务查询、闪回事务等都需要补充日志的支持。尤其是日志挖掘器如果发现未启用补充日志,就拒绝服务。
也就是说补充日志主要是为UPDATE 命令服务的,补充的目的是高度还原 UPDATE 命令,避免因为update 命令造成的行迁移和行移动,
让LogMiner 通过分析重做日志中识别 update 命令 不是 由 insert 和 delete 完成的。
如果未启用补充日志,重做日志只将 UPDATE 命令更改的字段的旧值保存在撤销数据块的变更矢量中。而在数据块中的变更矢量中记载被修改后的字段的新值。同行中的未被修改的字段,不会被记载。
如果启用了补充日志,重做日志中的撤销数据块的变更矢量中会记录被修改字段前的值和修改后的值,而且还会记录修改字段的那个条件的值。即变更矢量中会记载:几号数据文件+几号文件中的几号块+第几个字段+修改后的值+修改前的值+“where 条件” 的值。