oracle 序列值導致的主鍵衝突問題
背景:操作中我是先匯出了資料庫的結構(包括序列),再匯入資料(資料來自另一個庫)。這導致了部分表相應的序列值(比如表YK_YKLB,用序列SEQ_YK_YKLB的值來作為表的主鍵值)小於主鍵的最大值,這樣插入資料時會報錯主鍵衝突。
解決方法:希望將有問題的序列值增大到表的主鍵最大值+50
(當然啦有一種更簡單粗暴的方法,就是把所有的序列值統統往上加800 或者更大,但這種方法未必能解決所有問題,可能有漏網之魚。下面是一種更精準的方法,直接比較表主鍵最大值和相應序列值)
第一步:找出主鍵為單列的表(多列構成聯合主鍵的情況,插入資料時不會僅從序列取值作為主鍵,不會出現問題)和主鍵所在的列
SELECT max(COLUMN_NAME) col,max(TABLE_NAME) t
FROM user_cons_columns
WHERE constraint_name IN (select constraint_name from user_constraints WHERE constraint_type ='P') --篩選主鍵約束
GROUP BY CONSTRAINT_NAME
HAVING count(1) = 1
第二步:找出這些表的主鍵的最大值、表的序列的當前值
新建MAXVAL_SEQVAL_TABLE表,這個表包含3各欄位:MAXVAL(表的主鍵的最大值),SEQVAL(表的序列的當前值),TABLENAME(表名)
▲執行此語句 建表語句:
create table MAXVAL_SEQVAL_TABLE(MAXVAL varchar2(100),SEQVAL varchar2(100),TABLENAME varchar2(100));
將各個表的主鍵最大值、序列值、表名找出來,插入MAXVAL_SEQVAL_TABLE表。
▲執行此語句 查詢語句,拼接sql
select max(COLUMN_NAME) col, max(TABLE_NAME) t, 'INSERT INTO maxval_seqval_table SELECT (select max(' || max(COLUMN_NAME)|| ') from ' || max(TABLE_NAME)|| ') maxVal,SEQ_' || max(TABLE_NAME)|| '.nextval AS seqval, ''' || max(TABLE_NAME)|| ''' AS t FROM dual;'
from user_cons_columns
where constraint_name in (select constraint_name
from user_constraints
where constraint_type = 'P')--篩選主鍵約束
and ('SEQ_' || TABLE_NAME) in (select sequence_name
from USER_SEQUENCES)
group by CONSTRAINT_NAME
having count(1) = 1;
結果是這樣的:
▲執行此語句 將查詢結果的sql語句複製出來,執行
就將所有有用的資料插入到了MAXVAL_SEQVAL_TABLE表。表中的資料如下:
第三步:將序列(假定序列都是以規範的方式:SEQ_表名命名的)值改為相應表的主鍵最大值+50
▲執行此語句 新建一列MAXVAL_add,儲存主鍵值+50的值
alter table MAXVAL_SEQVAL_TABLE add MAXVAL_add number;
update MAXVAL_SEQVAL_TABLE set MAXVAL_add = TO_NUMBER(MAXVAL) + 50;
有了MAXVAL_SEQVAL_TABLE表,下面就很好辦了。刪除、重建序列的語句拼接一下
▲執行此語句 將下面查詢結果複製到sql視窗或控制檯中執行
SELECT
'DROP SEQUENCE SEQ_'||TABLENAME||';CREATE SEQUENCE SEQ_'||TABLENAME||' INCREMENT BY 1 MINVALUE 1 MAXVALUE 9999999999 NOCYCLE CACHE 20 NOORDER start WITH '||maxVal_Add||';'
FROM maxval_seqval_table WHERE TO_NUMBER(MAXVAL) >= TO_NUMBER(SEQVAL)
注意:這裡的條件一定要是TO_NUMBER(MAXVAL) >= TO_NUMBER(SEQVAL),不能少了TO_NUMBER!!否則就是字串的比較,沒有意義
查詢結果複製出來,執行
執行完後,可將MAXVAL_SEQVAL_TABLE表刪除,因為表中資料只能反映上次靜態收集的資訊。
所有有問題的序列值就都更改啦!再也不會有插入資料主鍵衝突的問題!
後來想了一下,這個方法也有一些缺陷,比如序列的命名是否規範。如果序列和表不是按照規範命名的,那就白搭了。。序列是怎麼命名的,還要研究一下orm框架,比如本專案用的是hibernate。
注意:此方法最好在業務停止或很少的時候執行,因為涉及到刪除與重建序列,可能與正在進行的業務衝突