視圖 是儲存查詢定義的虛擬表,不佔用資料儲存空間,卻能簡化存取、隱藏複雜度、實作安全控制。本文系統整理視圖建立語法、簡單/複雜視圖差異、DML 限制、WITH CHECK OPTION/WITH READ ONLY 保護機制,以及透過 INSTEAD OF 觸發器對複雜視圖進行寫入的完整模式。
SELECT * FROM v_emp_dept。CREATE OR REPLACE 平滑遷移,應用端無感。GRANT SELECT ON v_salary TO hr_role 只暴露必要欄位,隱藏薪資細節。CREATE [OR REPLACE] [FORCE | NOFORCE] VIEW view_name [(alias1, alias2, ...)] AS subquery [WITH CHECK OPTION [CONSTRAINT constraint_name]] [WITH READ ONLY];
| 選項 | 說明 |
|---|---|
OR REPLACE | 視圖存在則取代,保留權限授與;不存在則建立。 |
FORCE | 基礎表不存在也可建立視圖 (編譯時驗證延後到查詢時)。 |
NOFORCE (預設) | 基礎表必須存在,否則報錯。 |
| 別名清單 | 為子查詢欄位重新命名,長度/順序需一致。 |
WITH CHECK OPTION | 防止 DML 導致列「脫離視圖定義範圍」 (如 WHERE deptno=20 視圖中更新 deptno=30)。 |
WITH READ ONLY | 完全禁止視圖上的 DML,僅供查詢。 |
定義:單一基礎表、無彙總/分組/連接/DISTINCT/集合運算/子查詢/虛擬欄位。
-- 範例:薪資 > 2000 的員工視圖 CREATE VIEW v_emp_high_sal AS SELECT empno, ename, job, sal, deptno FROM emp WHERE sal > 2000; -- 直接 DML INSERT INTO v_emp_high_sal VALUES (9999, 'TEST', 'CLERK', 2500, 10); UPDATE v_emp_high_sal SET sal = 3000 WHERE empno = 9999; DELETE FROM v_emp_high_sal WHERE empno = 9999;
Oracle 會自動將 DML 轉譯至基礎表,並檢查 WITH CHECK OPTION 條件。
符合下列任一特徵即為複雜視圖:
UNION, MINUS...)SUM, AVG, COUNT...) 或 GROUP BYCONNECT BY, START WITH, DISTINCTCASE / DECODE 運算式-- 典型複雜視圖:部門彙總
CREATE OR REPLACE VIEW v_dept_summary AS
SELECT d.deptno, d.dname, d.loc,
COUNT(e.empno) AS cnt,
ROUND(AVG(e.sal),2) AS avg_sal,
SUM(e.sal) AS sum_sal
FROM dept d
LEFT JOIN emp e ON e.deptno = d.deptno
GROUP BY d.deptno, d.dname, d.loc;
對此視圖執行 INSERT/UPDATE/DELETE 會報 ORA-01779: cannot modify a column which maps to a non key-preserved table 或 ORA-01732: data manipulation operation not legal on this view。
INSTEAD OF 觸發器定義於視圖上,取代 原本的 DML 動作,由開發者撰寫 PL/SQL 決定如何同步更新多張基礎表。
CREATE [OR REPLACE] TRIGGER trig_name INSTEAD OF INSERT OR UPDATE OR DELETE ON view_name [FOR EACH ROW] [WHEN (condition)] DECLARE ... BEGIN -- :NEW 代表新值 (INSERT/UPDATE),:OLD 代表舊值 (UPDATE/DELETE) -- 根據 INSERTING/UPDATING/DELETING 判斷執行邏輯 END;
CREATE OR REPLACE VIEW v_emp_dept AS
SELECT e.empno, e.ename, e.job, e.sal, e.deptno, d.dname, d.loc
FROM emp e JOIN dept d ON e.deptno = d.deptno;
CREATE OR REPLACE TRIGGER v_emp_dept_iod_trg
INSTEAD OF INSERT OR UPDATE OR DELETE ON v_emp_dept
FOR EACH ROW
DECLARE
v_emp_cnt NUMBER;
v_dept_cnt NUMBER;
BEGIN
IF INSERTING THEN
-- 檢查員工是否存在
SELECT COUNT(*) INTO v_emp_cnt FROM emp WHERE empno = :NEW.empno;
-- 檢查部門是否存在
SELECT COUNT(*) INTO v_dept_cnt FROM dept WHERE deptno = :NEW.deptno;
IF v_emp_cnt = 0 THEN
INSERT INTO emp (empno, ename, job, sal, deptno)
VALUES (:NEW.empno, :NEW.ename, :NEW.job, :NEW.sal, :NEW.deptno);
END IF;
IF v_dept_cnt = 0 THEN
INSERT INTO dept (deptno, dname, loc)
VALUES (:NEW.deptno, :NEW.dname, :NEW.loc);
END IF;
ELSIF UPDATING THEN
UPDATE emp SET ename = :NEW.ename, job = :NEW.job, sal = :NEW.sal
WHERE empno = :NEW.empno;
UPDATE dept SET dname = :NEW.dname, loc = :NEW.loc
WHERE deptno = :NEW.deptno;
ELSIF DELETING THEN
-- 僅刪除員工,保留部門 (視業務決定)
DELETE FROM emp WHERE empno = :OLD.empno;
END IF;
END;
/
-- 測試
INSERT INTO v_emp_dept VALUES (8888, 'NEW', 'CLERK', 2000, 50, 'NEW_DEPT', 'TAIPEI');
UPDATE v_emp_dept SET sal = 2500, dname = 'UPD_DEPT' WHERE empno = 8888;
DELETE FROM v_emp_dept WHERE empno = 8888;
-- 強制 WHERE 條件不可被更新違反 CREATE OR REPLACE VIEW v_emp_20 AS SELECT * FROM emp WHERE deptno = 20 WITH CHECK OPTION CONSTRAINT v_emp_20_ck; -- 嘗試將 deptno 改為 30 → ORA-01402: view WITH CHECK OPTION where-clause violation UPDATE v_emp_20 SET deptno = 30 WHERE empno = 7369; -- 唯讀視圖 CREATE OR REPLACE VIEW v_emp_20_ro AS SELECT * FROM emp WHERE deptno = 20 WITH READ ONLY; -- 任何 DML → ORA-42399: cannot perform a DML operation on a read-only view
-- 列出當前使用者視圖 SELECT view_name, text_length, text, read_only FROM user_views; -- 刪除視圖 DROP VIEW view_name;
V__ (如 V_HR_EMP_SALARY)INSTEAD OF 觸發器並完整測試 INSERT/UPDATE/DELETE 路徑WITH CHECK OPTION 或 WITH READ ONLYUSER_DEPENDENCIES 確認視圖相依關係,避免基礎表 DDL 導致視圖失效MATERIALIZED VIEW (物化視圖) 提升效能善用視圖與 INSTEAD OF 觸發器,能在「資料模型正規化」與「應用存取簡單化」之間取得最佳平衡。