首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

Oracle 視圖插入資料完全指南:可更新視圖條件與 INSTEAD OF 觸發器實戰

     編輯:Oracle 資料庫相關知識

視圖可更新性判斷

查詢 USER_UPDATABLE_COLUMNS 確認:

SELECT table_name, column_name, updatable, insertable, deletable
FROM user_updatable_columns
WHERE table_name = 'YOUR_VIEW';

非連接視圖 (單表視圖)

  • 基於單一基礎表建立
  • 原則上皆可更新:支援 INSERT/UPDATE/DELETE
  • 例外:包含聚合、GROUP BY、DISTINCT、ROWMID、運算式等衍生欄位不可更新

連接視圖 (多表視圖) 更新規則

關鍵概念:**碼保留表** 與 **非碼保留表**

定義


-- 部門表 (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 重複)

INSERT 規則

  1. 不可明確或隱含引用 **非碼保留表** 的欄位
  2. 若視圖定義含 WITH CHECK OPTION,**完全禁止 INSERT**
  3. 僅能插入碼保留表的欄位,非碼保留表欄位由資料庫自動處理 (需有預設值或可為 NULL)

INSTEAD OF 觸發器:複雜視圖更新的標準解法

適用於:連接視圖、含運算式視圖、需自定義 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;
/

實戰範例:emp_dept 視圖


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;
/

完整 DML 支援範本


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+)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved