1. 介紹
觸發器(trigger)是數據庫提供給程序員和數據分析員來保證數據完整性的一種方法,它是與表事件相關的特殊的存儲過程,它的執行不是由程序調用,也不是手工啟動,而是由事件來觸發,比如當對一個表進行操作(insert,delete,update)時就會激活它執行。觸發器經常用於加強數據的完整性約束和業務規則等。
ORACLE觸發器有三種類型,分別是:DML觸發器、替代觸發器和系統觸發器。
DML觸發器
顧名思義,DML觸發器是由DML語句觸發的。例如數據庫的INSERT、UPDATE、DELETE操作都可以觸發該類型的觸發器。它們可以在這些語句之前或之后觸發,或者在行級上觸發(就是說對於每個受影響的行都觸發一次)。
替代觸發器
替代觸發器只能使用在視圖上,與DML不同的是,DML觸發器是運行在DML之外的,而替代觸發器是代替激發它的DML語句運行。替代觸發器是行觸發器。
系統觸發器
這種觸發器是發生在如數據庫啟動或關閉等系統事件時,不是在執行DML語句時發生,當然也可以在DDL時觸發。
觸發器功能強大,輕松可靠地實現許多復雜的功能,但是我們也應該慎用。為什么又要慎用呢?觸發器本身沒有過錯,但如果我們濫用,會造成數據庫及應用程序的維護困難。在數據庫操作中,我們可以通過關系、觸發器、存儲過程、應用程序等來實現數據操作,同時約束、缺省值也是保證數據完整性的重要保障。如果我們對觸發器過分的依賴,勢必影響數據庫的結構,同時增加了維護的復雜程度。
2. 觸發器組成
觸發器主要由以下幾個要素組成:
- 觸發事件:引起觸發器被觸發的事件。
- 觸發時間:觸發器是在觸發事件發生之前(BEFORE)還是之后(AFTER)觸發,也就是觸發事件和該觸發器的操作順序。
- 觸發操作:觸發器被觸發之后的目的和意圖,是觸發器本身要做的事情。
- 觸發對象:包括表、視圖、模式、數據庫。只有在這些對象上發生了符合觸發條件的觸發事件,才會執行觸發操作。
- 觸發條件:由WHEN子句指定一個邏輯表達式。只有當該表達式的值為TRUE時,遇到觸發事件才會自動執行觸發器,使其執行觸發操作。
- 觸發頻率:說明觸發器內定義的動作被執行的頻率。即語句級(STATEMENT)觸發器和行級(ROW)觸發器:
語句級(STATEMENT)觸發器:是指當某觸發事件發生時,該觸發器只執行一次;
行級(ROW)觸發器:是指當某觸發事件發生時,對受到該操作影響的每一行數據,觸發器都單獨執行一次。
3. 創建觸發器
語法:
1 CREATE [ OR REPLACE ] TRIGGER plsql_trigger_source
1 plsql_trigger_source ::======== 2 3 [schema.] trigger_name 4 { simple_dml_trigger 5 | instead_of_dml_trigger 6 | compound_dml_trigger 7 | system_trigger 8 } 9 10 simple_dml_trigger ::======== 11 12 { BEFORE | AFTER } dml_event_clause [ referencing_clause ] [ FOR EACH ROW ] 13 [ trigger_edition_clause ] [ trigger_ordering_clause ] 14 [ ENABLE | DISABLE ] [ WHEN ( condition ) ] trigger_body 15 16 instead_of_dml_trigger ::= 17 18 INSTEAD OF { DELETE | INSERT | UPDATE } [ OR { DELETE | INSERT | UPDATE } ]... 19 ON [ NESTED TABLE nested_table_column OF ] [ schema. ] noneditioning_view 20 [ referencing_clause ] [ FOR EACH ROW ] 21 [ trigger_edition_clause ] [ trigger_ordering_clause ] 22 [ ENABLE | DISABLE ] trigger_body 23 24 system_trigger ::======= 25 26 { BEFORE | AFTER | INSTEAD OF } 27 { ddl_event [OR ddl_event]... 28 | database_event [OR database_event]... 29 } 30 ON { [schema.] SCHEMA 31 | DATABASE 32 } 33 [ trigger_ordering clause ] 34 35 dml_event_clause ::============ 36 37 { DELETE | INSERT | UPDATE [ OF column [, column ]... ] } 38 [ OR { DELETE | INSERT | UPDATE [ OF column [, column]... ] }... 39 ON [ schema.] { table | view } 40 41 referencing_clause ::========== 42 43 REFERENCING 44 { OLD [ AS ] old 45 | NEW [ AS ] new 46 | PARENT [ AS ] parent 47 }... 48 49 trigger_body ::======= 50 51 { plsql_block | CALL routine_clause }
說明:
BEFORE和AFTER指出觸發器的觸發時間分別為前觸發和后觸發方式,前觸發是在執行觸發事件之前觸發當前所創建的觸發器,后觸發是在執行觸發事件之后觸發當前所創建的觸發器。
REFERENCING子句說明相關名稱,在行觸發器的PL/SQL塊和WHEN子句中可以使用相關名稱參照當前的新、舊列值,默認的相關名稱為OLD和NEW。觸發器的PL/SQL塊中應用相關名稱時,必須在它們之前加冒號(:),但在WHEN子句中則不能加冒號。
NEW只在UPDATE、INSERT的DML觸發器內可用,它包含了修改發生后被影響行的值。
OLD只在UPDATE、DELETE的DML觸發器內可用,它包含了修改發生前被影響行的值。
FOR EACH ROW選項說明觸發器為行觸發器。行觸發器和語句觸發器的區別表現在:行觸發器要求當一個DML語句操走影響數據庫中的多行數據時,對於其中的每個數據行,只要它們符合觸發約束條件,均激活一次觸發器;而語句觸發器將整個語句操作作為觸發事件,當它符合約束條件時,激活一次觸發器。當省略FOR EACH ROW 選項時,BEFORE和AFTER觸發器為語句觸發器,而INSTEAD OF觸發器則只能為行觸發器。
WHEN子句說明觸發約束條件。Condition為一個邏輯表達時,其中必須包含相關名稱,而不能包含查詢語句,也不能調用PL/SQL函數。WHEN子句指定的觸發約束條件只能用在BEFORE和AFTER行觸發器中,不能用在INSTEAD OF行觸發器和其它類型的觸發器中。
INSTEAD OF選項(創建替代觸發器)使ORACLE激活觸發器,而不執行觸發事件。只能對視圖和對象視圖建立INSTEAD OF觸發器,而不能對表、模式和數據庫建立INSTEAD OF觸發器。
ddl_event:一個或多個DDL事件,事件間用OR分開。
database_event:一個或多個數據庫事件,事件間用OR分開。
示例1,在插入數據時,自動使用序列編號:
1 CREATE OR REPLACE TRIGGER EMP_INSERT_ID 2 BEFORE INSERT ON employee FOR EACH ROW 3 BEGIN 4 SELECT SEQ_ID.NEXTVAL INTO :NEW.ID FROM DUAL; 5 END;
1 示例2,在多表聯接的視圖中插入數據: 2 3 -- 創建視圖 4 CREATE OR REPLACE VIEW vw_emp AS 5 SELECT e.name ename, e.address, d.name dname 6 FROM employee e, dept d 7 WHERE e.did = d.id; 8 9 -- 創建觸發器 10 CREATE TRIGGER emp_insert_trigger 11 INSTEAD OF INSERT ON vw_emp 12 DECLARE 13 v_did dept.id%TYPE; 14 BEGIN 15 SELECT id INTO v_did FROM dept WHERE name = :NEW.dname; 16 INSERT INTO emp (name, address, did) VALUES (:NEW.ename, :NEW.address, v_did); 17 END emp_insert_trigger;
1 示例3,創建實例啟動觸發器: 2 3 -- 創建記錄操作事件的表 4 CREATE TABLE event_table( 5 event VARCHAR2(50), 6 time DATE 7 ); 8 9 -- 創建觸發器 10 CREATE OR REPLACE TRIGGER tr_startup 11 AFTER STARTUP 12 ON DATABASE 13 BEGIN 14 INSERT INTO event_table(event, time) 15 VALUES(ora_sysevent, SYSDATE); 16 END;
DML觸發器
DML觸發器對我們開發人員來說是最常用的。DML觸發器是由數據庫的INSERT、UPDATE、DELETE操作觸發,該類觸發器可以在上述語句之前或之后執行,也可以每個受影響的行執行一次。
條件謂詞:當在觸發器中包含多個觸發事件(INSERT、UPDATE、DELETE)的組合時,為了分別針對不同的事件進行不同的處理,需要使用ORACLE提供的條件謂詞:
- INSERTING:當觸發事件是INSERT時,取值為TRUE,否則為FALSE。
- UPDATING [(column_1,column_2,…,column_x)]:當觸發事件是UPDATE時,如果修改了column_x列,則取值為TRUE,否則為FALSE。
- DELETING:當觸發事件是DELETE時,則取值為TRUE,否則為FALSE。
示例:
1 CREATE OR REPLACE TRIGGER emp_sal_trigger 2 BEFORE UPDATE OF salary OR DELETE 3 ON employee FOR EACH ROW 4 WHEN (old.did = 1) 5 BEGIN 6 CASE 7 WHEN UPDATING ('salary') THEN 8 IF :NEW.salary < :old.salary THEN 9 RAISE_APPLICATION_ERROR(-20001, '部門1的員工工資不能降'); 10 END IF; 11 WHEN DELETING THEN 12 RAISE_APPLICATION_ERROR(-20002, '不能刪除部門1的員工記錄'); 13 END CASE; 14 END emp_sal_trigger;
5. 替代觸發器
INSTEAD OF用於對視圖的DML觸發,由於視圖有可能是由多個表聯結(JOIN)而成,因而並非所有的視圖都是可更新的,但可以按照所需的方式執行更新。
創建INSTEAD OF觸發器需要注意以下幾點:
- 只能被創建在視圖上,並且該視圖沒有指定WITH CHECK OPTION選項。
- 不能指定BEFORE或AFTER選項。
- FOR EACH ROW子句是可選的。
- 沒有必要在針對一個表的視圖上創建INSTEAD OF觸發器,只要創建DML觸發器就可以了。
示例:
1 CREATE OR REPLACE TRIGGER emp_delete_trigger 2 INSTEAD OF DELETE ON vw_emp FOR EACH ROW 3 DECLARE 4 v_did dept.id%TYPE; 5 BEGIN 6 SELEC id INTO v_did FROM dept WHERE name=:OLD.dname; 7 DELETE FROM employee WHERE did= v_did; 8 END emp_delete_trigger;
6. 系統觸發器
系統觸發器可以在DDL或數據庫系統上被觸發,數據庫系統事件包括數據庫服務器的啟動或關閉,用戶的登錄與退出、數據庫服務錯誤等。
系統事件觸發器既可以建立在一個模式上,又可以建立在整個數據庫上。當建立在模式(SCHEMA)之上時,只有模式所指定用戶的DDL操作和它們所導致的錯誤才激活觸發器,默認時為當前用戶模式。當建立在數據庫(DATABASE)之上時,該數據庫所有用戶的DDL操作和他們所導致的錯誤,以及數據庫的啟動和關閉均可激活觸發器。
系統觸發器的種類和事件出現的時機:
1 -- 創建記錄用戶登錄注銷日志的表 2 CREATE TABLE log_on_off_log 3 (user_name VARCHAR2(20), 4 logon_date timestamp, 5 logoff_date timestamp); 6 7 -- 創建登錄觸發器 8 CREATE OR REPLACE TRIGGER logon_trigger 9 AFTER LOGON ON DATABASE 10 BEGIN 11 INSERT INTO log_on_off_log (user_name, logon_date) VALUES (ora_login_user, systimestamp); 12 END logon_trigger; 13 14 -- 創建退出觸發器 15 CREATE OR REPLACE TRIGGER logoff_trigger 16 BEFORE LOGOFF ON DATABASE 17 BEGIN 18 INSERT INTO log_on_off_log (user_name, logoff_date) VALUES (ora_login_user, systimestamp); 19 END logoff_trigger;
| 事件 | 觸發時機 | 說明 |
|---|---|---|
| STARTUP | AFTER | 啟動數據庫實例之后觸發 |
| SHUTDOWN | BEFORE | 關閉數據庫實例之前觸發 |
| SERVERERROR | AFTER | 數據庫服務器發生錯誤之后觸發 |
| LOGON | AFTER | 成功登錄到數據庫后觸發 |
| LOGOFF | BEFORE | 斷開數據庫連接之前觸發 |
| DDL | BEFORE,AFTER | 在執行大多數DDL語句之前、之后觸發 |
| CREATE / ALTER / DROP | BEFORE,AFTER | 在執行CREATE或ALTER或DROP語句創建數據庫對象之前、之后觸發 |
| RENAME | BEFORE,AFTER | 執行RENAME語句更改數據庫對象名稱之前、之后觸發 |
| GRANT / REVOKE | BEFORE,AFTER | 執行GRANT語句授予權限或REVOKE撤銷權限之前、之后觸發 |
| AUDIT / NOAUDIT | BEFORE,AFTER | 執行AUDIT或NOAUDIT進行審計或停止審計之前、之后觸發 |
