Oracle 單表視圖支持直接 DML,但多表連接視圖默認不可更新。本文展示如何通過 INSTEAD OF 觸發器攔截視圖上的 INSERT/UPDATE/DELETE,將操作分發到底層基表,實現透明的視圖寫入。
-- 假設視圖 V_EMP_DEPT 由 EMP、DEPT 兩表連接而成
CREATE VIEW v_emp_dept AS
SELECT e.empno, e.ename, e.sal, d.deptno, d.dname
FROM emp e JOIN dept d ON e.deptno = d.deptno;
-- 直接插入會報錯
INSERT INTO v_emp_dept VALUES (9999, 'TEST', 5000, 10, 'ACCOUNTING');
-- ORA-01776: cannot modify more than one base table through a join view
CREATE OR REPLACE TRIGGER trg_v_emp_dept_io
INSTEAD OF INSERT OR UPDATE OR DELETE ON v_emp_dept
FOR EACH ROW
DECLARE
BEGIN
IF INSERTING THEN
-- 插入基表 EMP
INSERT INTO emp (empno, ename, sal, deptno)
VALUES (:NEW.empno, :NEW.ename, :NEW.sal, :NEW.deptno);
-- 插入基表 DEPT(若部門不存在)
-- 實際中可先判斷 DEPT 是否存在,避免主鍵衝突
INSERT INTO dept (deptno, dname)
VALUES (:NEW.deptno, :NEW.dname);
ELSIF UPDATING THEN
UPDATE emp
SET ename = :NEW.ename, sal = :NEW.sal, deptno = :NEW.deptno
WHERE empno = :OLD.empno;
UPDATE dept
SET dname = :NEW.dname
WHERE deptno = :OLD.deptno;
ELSIF DELETING THEN
DELETE FROM emp WHERE empno = :OLD.empno;
DELETE FROM dept WHERE deptno = :OLD.deptno;
END IF;
END;
/
| 要素 | 說明 |
|---|---|
INSTEAD OF | 標識此觸發器替代默認 DML 行為,僅適用於視圖,不適用於表。 |
FOR EACH ROW | 行級觸發,:NEW 代表新值,:OLD 代表舊值。 |
INSERTING/UPDATING/DELETING | 判斷觸發事件類型的布爾函數。 |
| 異常處理 | 建議在每個 DML 塊添加 EXCEPTION WHEN OTHERS THEN ... 記錄錯誤並拋出,防止數據不一致。 |
| 事務控制 | 觸發器隱式運行於觸發語句的事務中,不可顯式 COMMIT/ROLLBACK(除非使用自主事務)。 |
-- 插入測試
INSERT INTO v_emp_dept (empno, ename, sal, deptno, dname)
VALUES (9999, 'TRIGGER_TEST', 6000, 50, 'NEW_DEPT');
-- 查詢基表確認
SELECT * FROM emp WHERE empno = 9999;
SELECT * FROM dept WHERE deptno = 50;
-- 更新測試
UPDATE v_emp_dept SET sal = 7000 WHERE empno = 9999;
-- 刪除測試
DELETE FROM v_emp_dept WHERE empno = 9999;
ON DELETE CASCADE。EDITIONABLE VIEW 與 BEQUEATH CURRENT_USER,但 INSTEAD OF 仍是通用標準方案。
通過 INSTEAD OF 觸發器,多表視圖即刻獲得完整的 DML 能力,極大簡化應用層邏輯,實現「視圖即服務」的數據訪問模式。