業務有時需要修改主鍵值,但當該主鍵被其他表的外鍵引用時,直接 UPDATE 會觸發完整性約束違規(ORA-02292)。Oracle 不支援 ON UPDATE CASCADE,需人工協調處理。
-- 學生表(主鍵 S#) CREATE TABLE Student ( S# INTEGER PRIMARY KEY, sname VARCHAR2(20), age INTEGER ); -- 課程表(主鍵 C#) CREATE TABLE Course ( C# INTEGER PRIMARY KEY, cname VARCHAR2(20), credit INTEGER ); -- 成績表(雙外鍵) CREATE TABLE SC ( S# INTEGER, C# INTEGER, score INTEGER, CONSTRAINT fk_sc_stu FOREIGN KEY (S#) REFERENCES Student(S#) ON DELETE CASCADE, CONSTRAINT fk_sc_cou FOREIGN KEY (C#) REFERENCES Course(C#) ON DELETE CASCADE );
需求:修改學生學號(S#),同步更新 SC 表關聯記錄。
| 方案 | 步驟 | 優點 | 缺點 |
|---|---|---|---|
| 禁用約束法 | 1. 禁用 FK 2. 更新主表 3. 更新子表 4. 啟用 FK | 邏輯簡單、效能高 | 需 DDL 權限、暫時失去約束保護 |
| 中間表法 | 1. 子表數據存入臨時表 2. 刪除子表記錄 3. 更新主表 4. 修改臨時表並回寫 | 全程保持約束啟用 | 操作複雜、臨時表管理麻煩 |
ALTER TABLE SC ADD CONSTRAINT fk_sc_stu FOREIGN KEY (S#) REFERENCES Student(S#);
CREATE OR REPLACE PROCEDURE change_student_id(
p_old_id IN INTEGER,
p_new_id IN INTEGER
) AS
BEGIN
-- 1. 禁用外鍵約束(動態 SQL)
EXECUTE IMMEDIATE 'ALTER TABLE SC DISABLE CONSTRAINT fk_sc_stu';
-- 2. 更新主表主鍵
UPDATE Student SET S# = p_new_id WHERE S# = p_old_id;
-- 3. 更新子表外鍵
UPDATE SC SET S# = p_new_id WHERE S# = p_old_id;
-- 4. 重新啟用約束(會驗證現有數據)
EXECUTE IMMEDIATE 'ALTER TABLE SC ENABLE CONSTRAINT fk_sc_stu';
COMMIT;
EXCEPTION
WHEN OTHERS THEN
-- 異常時嘗試恢復約束
BEGIN
EXECUTE IMMEDIATE 'ALTER TABLE SC ENABLE CONSTRAINT fk_sc_stu';
EXCEPTION WHEN OTHERS THEN NULL;
END;
RAISE;
END;
/
EXECUTE IMMEDIATEDISABLE/ENABLE CONSTRAINT 會獲取獨佔 DDL 鎖,高併發環境需謹慎使用DBMS_REDEFINITION 線上重定義表結構