觸發器 vs 預存程序:核心差異
| 維度 | 觸發器 | 預存程序 |
| 觸發方式 | 事件自動觸發 | 名稱顯式呼叫 |
| 事務控制 | 不能含 COMMIT/ROLLBACK/SAVEPOINT | 可自主控制事務 |
| 參數傳遞 | 無參數,透過 :NEW/:OLD 存取資料 | 支援 IN/OUT/IN OUT 參數 |
| 返回值 | 無 | Function 可返回值 |
四大觸發器類型
- DML 觸發器:定義於表,響應 INSERT/UPDATE/DELETE。
- INSTEAD OF 觸發器:定義於**視圖**,替代對視圖的 DML 操作(實現不可更新視圖的更新)。
- DDL 觸發器:定義於 Schema/Database,響應 CREATE/ALTER/DROP 等結構變更。
- 資料庫事件觸發器:定義於 Database/Schema,響應 STARTUP/SHUTDOWN/LOGON/LOGOFF/SERVERERROR 等系統事件。
標準建立語法
CREATE [OR REPLACE] TRIGGER trigger_name
{BEFORE | AFTER | INSTEAD OF}
event1 [OR event2 ...]
ON table_name | view_name | schema | database
[WHEN (condition)]
[FOR EACH ROW] -- 行級,省略則語句級
DECLARE
-- 宣告區
BEGIN
-- 主體:PL/SQL 區塊
-- 可用 :NEW.col / :OLD.col 存取新舊值
-- 可用 INSERTING/UPDATING/DELETING 判斷觸發事件
END;
關鍵子句深度解析
觸發時機:BEFORE vs AFTER
- BEFORE:DML 執行前。行級 BEFORE → 每行操作前各執行一次。
- AFTER:DML 執行後。行級 AFTER → 每行操作後各執行一次;語句級 AFTER → 整個語句結束後執行一次。
- 執行順序:**BEFORE 行級 → DML 操作 → AFTER 行級 → AFTER 語句級**。
觸發層級:FOR EACH ROW
- **行級:** 影響多少行就執行多少次,可存取 :NEW/:OLD,適合資料驗證、衍生欄位、審計。
- **語句級:** 整個語句僅觸發一次,無法存取 :NEW/:OLD,適合安全審計、統計維護、跨表同步。
INSTEAD OF 特殊機制
僅用於視圖,**替代** 原本 DML。視圖本身不儲存資料,觸發器負責將操作轉發到底表。實現「可更新視圖」的關鍵。
管理指令
-- 刪除
DROP TRIGGER trigger_name;
-- 啟用/停用
ALTER TRIGGER trigger_name ENABLE | DISABLE;
-- 查看編譯錯誤
SHOW ERRORS TRIGGER trigger_name;
觸發器的六大核心功能
- 強化約束:實現 CHECK 無法表達的跨表/跨行/複雜業務規則。
- 審計追蹤:記錄誰在何時做了什麼變更(寫入審計表)。
- 級聯操作:一表觸發器操作另一表 → 觸發另一表觸發器 → 連鎖反應(謹慎使用,難追蹤)。
- 呼叫預存程序:觸發器中調用封裝好的業務邏輯程序。
- 衍生欄位自動生成:如自動填入建立時間、修改人、序號(12c+ 建議用 IDENTITY)。
- 防止無效事務:BEFORE 觸發器中 RAISE_APPLICATION_ERROR 終止違規 DML。
效能陷阱與避坑指南
⚠ 觸發器效能通常較低,主要耗時在參照其他表(非記憶體、非快取)。
- 被參照表不在記憶體 → 實體 I/O → 嚴重拖慢 DML 響應。
- 刪除/插入暫存表(:OLD/:NEW)在記憶體中,速度快。
濫用後果
- 維護困難:業務邏輯分散在觸發器、預存程序、應用程式多層,難追蹤。
- 除錯噩夢:觸發器隱式執行,單步調試極其困難。
- 死鎖風險:級聯觸發器易形成循環鎖等待。
最佳實踐原則
- 能用約束解決,絕不用觸發器。(PK/FK/UK/CHECK/NOT NULL)
- 能用應用層解決,優先用應用層。(單純計算、格式轉換、簡單驗證)
- 必須用觸發器時:邏輯集中、文檔完善、命名規範(
TRG_表名_事件_層級)、避免跨表操作、嚴禁 COMMIT/ROLLBACK。
- 12c+ 新特性:IDENTITY 欄位、VIRTUAL COLUMN、DEFAULT ON NULL 可替代大量衍生欄位觸發器。