查詢 USER_UPDATABLE_COLUMNS 確認:
SELECT table_name, column_name, updatable, insertable, deletable
FROM user_updatable_columns
WHERE table_name = 'YOUR_VIEW';
關鍵概念:**碼保留表** 與 **非碼保留表**
-- 部門表 (DEPTNO 為主鍵)
-- 員工表 (EMPNO 為主鍵)
CREATE VIEW emp_dept AS
SELECT e.empno, e.ename, e.deptno, d.dname
FROM emp e, dept d
WHERE e.deptno = d.deptno;
EMP,EMPNO 仍唯一)DEPT,DEPTNO 重複)WITH CHECK OPTION,**完全禁止 INSERT**適用於:連接視圖、含運算式視圖、需自定義 DML 邏輯
CREATE OR REPLACE TRIGGER view_name_io_trg
INSTEAD OF INSERT OR UPDATE OR DELETE ON view_name
FOR EACH ROW
DECLARE
v_var TYPE;
BEGIN
IF INSERTING THEN
-- 解析 :NEW 欄位,分別插入基礎表
INSERT INTO base_table1 (...) VALUES (...);
INSERT INTO base_table2 (...) VALUES (...);
ELSIF UPDATING THEN
UPDATE base_table1 SET ... WHERE ...;
UPDATE base_table2 SET ... WHERE ...;
ELSIF DELETING THEN
DELETE FROM base_table1 WHERE ...;
DELETE FROM base_table2 WHERE ...;
END IF;
END;
/
CREATE OR REPLACE TRIGGER emp_dept_io_trg
INSTEAD OF INSERT OR UPDATE OR DELETE ON emp_dept
FOR EACH ROW
BEGIN
IF INSERTING THEN
-- 必須先插入非碼保留表 (部門),再插入碼保留表 (員工)
-- 這裡假設部門已存在,或需先檢查/建立
INSERT INTO emp (empno, ename, deptno)
VALUES (:NEW.empno, :NEW.ename, :NEW.deptno);
ELSIF UPDATING THEN
UPDATE emp
SET ename = :NEW.ename,
deptno = :NEW.deptno
WHERE empno = :OLD.empno;
ELSIF DELETING THEN
DELETE FROM emp WHERE empno = :OLD.empno;
END IF;
END;
/
CREATE OR REPLACE TRIGGER complex_view_io_trg
INSTEAD OF INSERT OR UPDATE OR DELETE ON complex_view
FOR EACH ROW
DECLARE
v_cnt NUMBER;
BEGIN
IF INSERTING THEN
-- 1. 處理主表
INSERT INTO main_table (id, col1, col2, created_at)
VALUES (:NEW.id, :NEW.col1, :NEW.col2, SYSDATE);
-- 2. 處理關聯表 (可選)
IF :NEW.related_id IS NOT NULL THEN
INSERT INTO related_table (main_id, attr)
VALUES (:NEW.id, :NEW.related_attr);
END IF;
ELSIF UPDATING THEN
UPDATE main_table
SET col1 = :NEW.col1,
col2 = :NEW.col2,
updated_at = SYSDATE
WHERE id = :OLD.id;
-- 同步關聯表
UPDATE related_table
SET attr = :NEW.related_attr
WHERE main_id = :OLD.id;
ELSIF DELETING THEN
-- 級聯刪除 (順序重要)
DELETE FROM related_table WHERE main_id = :OLD.id;
DELETE FROM main_table WHERE id = :OLD.id;
END IF;
EXCEPTION
WHEN OTHERS THEN
-- 記錄錯誤日誌
RAISE; -- 重拋讓呼叫端知曉
END;
/
| 建議 | 說明 |
|---|---|
| 優先用基礎表直接操作 | 視圖更新有額外開銷,核心業務直接操作表 |
| INSTEAD OF 觸發器保持精簡 | 避免複雜邏輯,必要時呼叫封裝程序 |
| 明確處理 :OLD/:NEW NULL 值 | INSERT 時 :OLD 為 NULL,DELETE 時 :NEW 為 NULL |
| 注意遞迴觸發 | 觸發器內操作同視圖會再次觸發 → 使用條件判斷或 PRAGMA AUTONOMOUS_TRANSACTION |
| 批次操作效能 | INSTEAD OF 為 ROW LEVEL,大量資料考慮暫停觸發器改用直接表操作 |
| 錯誤 | 原因 | 解決 |
|---|---|---|
| ORA-01779: cannot modify a column which maps to a non key-preserved table | 嘗試更新非碼保留表欄位 | 改用 INSTEAD OF 觸發器或直接操作基礎表 |
| ORA-01732: data manipulation operation not legal on this view | 視圖不可更新 (含聚合、DISTINCT 等) | 建立 INSTEAD OF 觸發器 |
| ORA-01402: view WITH CHECK OPTION where-clause violation | 違反視圖 CHECK OPTION 條件 | 檢查視圖定義條件、調整資料 |
| ORA-04091: table is mutating | 觸發器中查詢/修改觸發表 | 重構邏輯、使用複合觸發器 (11g+) |