掌握觸發器進階特性,能編寫更清晰、可控、高性能的觸發邏輯。本文深入講解 REFERENCING 別名機制、WHEN 條件過濾、三大觸發器謂詞(INSERTING/UPDATING/DELETING)及 FOLLOWS 執行順序控制。
當觸發器邏輯複雜,直接使用 :OLD.col、:NEW.col 可讀性差時,可定義別名:
CREATE OR REPLACE TRIGGER myempaddsal_trigger
BEFORE UPDATE ON myemp
REFERENCING OLD AS myemp_old NEW AS myemp_new
FOR EACH ROW
DECLARE
BEGIN
IF ABS((:myemp_new.sal - :myemp_old.sal) / :myemp_old.sal) > 0.1 THEN
RAISE_APPLICATION_ERROR(-20005, '工資最大漲幅不能超過 10%');
END IF;
END;
此處 :myemp_new.sal 等價於 :NEW.sal,語義更清晰。
WHEN 子句在觸發器體執行前評估,條件為 FALSE 則跳過觸發器體,減少開銷。注意:WHEN 中引用 :OLD/:NEW 時可省略冒號。
CREATE OR REPLACE TRIGGER myemptestadd1
BEFORE INSERT ON myemp
FOR EACH ROW
WHEN (NEW.sal = 0)
DECLARE
BEGIN
RAISE_APPLICATION_ERROR(-20003, :NEW.empno || ' 的工資為 0,不合規定!');
END;
CREATE OR REPLACE TRIGGER myemptestadd1
BEFORE UPDATE ON myemp
FOR EACH ROW
WHEN (NEW.sal < OLD.sal)
DECLARE
BEGIN
RAISE_APPLICATION_ERROR(-20003, :OLD.empno || ' 的工資只能漲不能降!');
END;
在 FOR EACH ROW 觸發器中,三個布爾謂詞僅有一個為 TRUE:
| 謂詞 | 觸發事件為 | 返回 |
|---|---|---|
| INSERTING | INSERT | TRUE |
| UPDATING | UPDATE | TRUE |
| DELETING | DELETE | TRUE |
-- 日志表
CREATE TABLE deptlog (
logid NUMBER PRIMARY KEY,
type VARCHAR2(20) NOT NULL,
deptno NUMBER(2),
logdate DATE,
dname VARCHAR2(14),
loc VARCHAR2(13)
);
CREATE SEQUENCE deptlog_seq;
-- 觸發器
CREATE OR REPLACE TRIGGER dept_trigger
BEFORE INSERT OR UPDATE OR DELETE ON dept
FOR EACH ROW
BEGIN
IF INSERTING THEN
INSERT INTO deptlog VALUES (deptlog_seq.NEXTVAL, 'INSERT', :NEW.deptno, SYSDATE, :NEW.dname, :NEW.loc);
ELSIF UPDATING THEN
INSERT INTO deptlog VALUES (deptlog_seq.NEXTVAL, 'UPDATE', :NEW.deptno, SYSDATE, :NEW.dname, :NEW.loc);
ELSIF DELETING THEN
INSERT INTO deptlog VALUES (deptlog_seq.NEXTVAL, 'DELETE', :OLD.deptno, SYSDATE, :OLD.dname, :OLD.loc);
END IF;
END;
同一表、同一時機、同一粒度的多個觸發器,默認執行順序不確定。使用 FOLLOWS 顯式聲明順序:
-- 觸發器 1
CREATE OR REPLACE TRIGGER dept1
BEFORE INSERT OR UPDATE OR DELETE ON dept
FOR EACH ROW
BEGIN
DBMS_OUTPUT.PUT_LINE('執行第 1 個觸發器 dept1');
END;
-- 觸發器 2:跟隨 dept1
CREATE OR REPLACE TRIGGER dept2
BEFORE INSERT OR UPDATE OR DELETE ON dept
FOR EACH ROW FOLLOWS dept1
BEGIN
DBMS_OUTPUT.PUT_LINE('執行第 2 個觸發器 dept2');
END;
-- 觸發器 3:跟隨 dept2
CREATE OR REPLACE TRIGGER dept3
BEFORE INSERT OR UPDATE OR DELETE ON dept
FOR EACH ROW FOLLOWS dept2
BEGIN
DBMS_OUTPUT.PUT_LINE('執行第 3 個觸發器 dept3');
END;
測試結果: 任意 DML 操作均按 1→2→3 順序輸出。
REFERENCING 提升可維護性。WHEN 過濾無效觸發,降低開銷。INSERTING/UPDATING/DELETING 分支處理。FOLLOWS 固化順序,避免升級/遷移後順序變更導致邏輯錯誤。