一、問題產生背景
進行指定表中某個字段的數據類型變更時,由於該字段中存在歷史數據,因此需要:
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 條件” 的值。