觸發器是特定事件自動執行的 PL/SQL 區塊,類似存儲過程但無法直接呼叫,由資料庫事件隱式觸發。包含宣告、執行、異常處理三段。
| 類型 | 建立對象 | 觸發事件 | 典型用途 |
|---|---|---|---|
| DML 觸發器 | 表 | INSERT/UPDATE/DELETE | 審計、衍生欄位維護、複雜驗證 |
| INSTEAD OF 觸發器 | 視圖 | INSERT/UPDATE/DELETE | 使不可更新視圖可更新 |
| DDL 觸發器 | 資料庫/Schema | CREATE/ALTER/DROP | 結構變更審計、防誤刪 |
| 資料庫事件觸發器 | 資料庫/Schema | STARTUP/SHUTDOWN/LOGON/LOGOFF/ERROR | 登入審計、啟動初始化 |
| 複合觸發器 (11g+) | 表/視圖 | DML 多時間點 | 解決 ORA-04091 突變表問題 |
CREATE [OR REPLACE] TRIGGER [schema.]trigger_name
{BEFORE | AFTER | INSTEAD OF}
{DELETE | INSERT | UPDATE [OF column...]} [OR ...]
ON [schema.]table_name | view_name
[FOR EACH ROW [WHEN (condition)]]
[DECLARE ...]
BEGIN
-- PL/SQL 區塊
END;
/
SHOW ERRORS
CREATE [OR REPLACE] TRIGGER trigger_name
INSTEAD OF {INSERT | DELETE | UPDATE [OF column...]} [OR ...]
ON view_name
[REFERENCING {OLD [AS] old | NEW [AS] new | PARENT AS parent}]
FOR EACH ROW -- 必須為行級
[DECLARE ...]
BEGIN
-- 視圖操作邏輯
END;
/
-- 注意:INSTEAD OF 無 BEFORE/AFTER,無 WHEN 條件
CREATE TABLE triggertest ( id VARCHAR2(20), name VARCHAR2(20), score NUMBER ); CREATE TABLE tab1 AS SELECT * FROM triggertest WHERE 1=0;
CREATE OR REPLACE TRIGGER trigger1
AFTER INSERT ON triggertest
FOR EACH ROW
BEGIN
INSERT INTO tab1 (id, name) VALUES ('22', '33');
END;
/
-- 測試
INSERT INTO triggertest (id) VALUES ('aabbcc');
-- tab1 新增一筆 ('22','33')
CREATE OR REPLACE TRIGGER trigger1
BEFORE INSERT OR DELETE OR UPDATE ON triggertest
FOR EACH ROW
BEGIN
IF (TO_CHAR(SYSDATE, 'DY', 'NLS_DATE_LANGUAGE=AMERICAN') IN ('SAT','SUN'))
OR (TO_CHAR(SYSDATE, 'HH24:MI') NOT BETWEEN '09:00' AND '18:00') THEN
RAISE_APPLICATION_ERROR(-20001, '非上班時間,禁止修改 triggertest');
END IF;
END;
/
-- 測試 (假設現在是週三 10:00)
INSERT INTO triggertest (id) VALUES ('test'); -- 成功
-- 若是週六或 20:00 → ORA-20001
CREATE OR REPLACE TRIGGER trigger1
BEFORE INSERT OR DELETE OR UPDATE ON triggertest
FOR EACH ROW
WHEN (OLD.name = '月兒')
BEGIN
IF UPDATING('score') THEN
IF :NEW.score < :OLD.score THEN
RAISE_APPLICATION_ERROR(-20001, '月兒的分數只能提升不能下降');
END IF;
END IF;
END;
/
-- 測試
UPDATE triggertest SET score = 10 WHERE name = '月兒'; -- ORA-20001 (原 20)
UPDATE triggertest SET score = 30 WHERE name = '月兒'; -- 成功
CREATE OR REPLACE TRIGGER trigger1
BEFORE INSERT OR DELETE OR UPDATE ON triggertest
FOR EACH ROW
BEGIN
testpro1;
END;
/
CREATE OR REPLACE PROCEDURE testpro1 AS
BEGIN
INSERT INTO tab1 (id, name, score) VALUES ('AAA', 'BBB', 200);
END;
/
-- 任何 DML 觸發 trigger1 皆會在 tab1 新增一筆
CREATE OR REPLACE TRIGGER trigger1 AFTER UPDATE OF name ON triggertest FOR EACH ROW BEGIN UPDATE tab1 SET name = :NEW.name WHERE name = :OLD.name; END; / -- 測試 UPDATE triggertest SET name = '水兒' WHERE name = '月兒'; -- tab1 中 name='月兒' 同步改為 '水兒'
-- 基礎表 CREATE TABLE student ( id NUMBER, name VARCHAR2(20), classid VARCHAR2(10), score NUMBER ); -- 視圖:各班平均分 CREATE OR REPLACE VIEW student_view AS SELECT classid, AVG(score) AS average_score FROM student GROUP BY classid; -- 直接刪除視圖會報錯 ORA-01732 DELETE FROM student_view WHERE classid = '111'; -- 解決:建立 INSTEAD OF DELETE 觸發器 CREATE OR REPLACE TRIGGER student_view_delete INSTEAD OF DELETE ON student_view FOR EACH ROW BEGIN DELETE FROM student WHERE classid = :OLD.classid; END; / -- 再次執行刪除 → 成功,student 表對應班級資料被刪除