【DB筆試面試448】Oracle中有哪幾類觸發器?
- 2019 年 10 月 11 日
- 筆記
Oracle中有哪幾類觸發器?
答案部分
Oracle資料庫有4種觸發器,分別是DML、替代觸發器(INSTEAD OF觸發器)、DDL和系統觸發器,一般的應用系統中都使用到DML、替代觸發器,而DDL和系統觸發器是DBA管理資料庫用得比較多。下圖是Oracle觸發器的分類圖:

圖 3-2 Oracle中觸發器的分類
觸發器(TRIGGER)的組成主要有以下幾部分:
① 觸發事件:即在何種情況下觸發TRIGGER。例如:DML語句(INSERT、UPDATE和DELETE語句對錶或視圖執行數據處理操作)、DDL語句(如CREATE、ALTER、DROP語句等在資料庫中創建、修改、刪除模式對象)、資料庫系統事件(如系統啟動或退出、異常錯誤)、用戶事件(如登錄或退出資料庫)。既可以是單個觸發事件,也可以是多個觸發事件的組合(只能使用OR邏輯組合,不能使用AND邏輯組合)。
② 觸發時間(觸發時機):即該TRIGGER是在觸發事件發生之前(BEFORE)還是之後(AFTER)觸發,也就是觸發事件和該TRIGGER的操作順序。如果指定為BEFORE,那麼表示在執行DML操作之前觸發,以便防止某些錯誤操作發生或實現某些業務規則;如果指定為AFTER,那麼表示在執行DML操作之後觸發,以便記錄該操作或做某些事後處理。
③ 觸發器本身:即該TRIGGER被觸發之後的目的和意圖,正是觸發器本身要做的事情。例如:PL/SQL塊。
④ 觸發頻率:說明觸發器內定義的動作被執行的次數,分為語句級(STATEMENT)觸發器和行級(ROW)觸發器。
a. 語句級(STATEMENT)觸發器:是指當某觸發事件發生時,該觸發器只執行一次;語句級觸發器不允許和WHEN子句一起使用。
b. 行級(ROW)觸發器:是指當某觸發事件發生時,對受到該操作影響的每一行數據,觸發器都單獨執行一次。
c. 當某操作隻影響到表中的一行數據時,語句級觸發器與行級觸發器的效果相同。
換句話說,語句級觸發器針對某一條語句只觸發一次,而行級觸發器則針對語句所影響的每一行都觸發一次。例如:某條UPDATE語句修改了表中的100行數據,那麼針對該UPDATE事件的語句級觸發器將被觸發一次,而行級觸發器將被觸發100次。
⑤ 觸發對象:包括表、視圖、用戶、資料庫。只有在這些對象上發生了符合觸發條件的觸發事件時,才會執行觸發操作。
⑥ 觸發條件:由WHEN子句指定一個邏輯表達式。只有當該表達式的值為TRUE時,遇到觸發事件才會自動執行觸發器,使其執行觸發操作。
下面分別介紹這4類觸發器。
1、DML觸發器
DML觸發器由DML語句觸發,例如,INSERT、UPDATE和DELETE語句。針對所有的DML事件,按觸發的時間可以將DML觸發器分為BEFORE觸發器和AFTER觸發器,另外,DML觸發器也可以分為語句級觸發器與行級觸發器。
創建DML觸發器的一般語法如下所示:
CREATE [OR REPLACE] TRIGGER trigger_name {BEFORE | AFTER } {INSERT | DELETE | UPDATE [OF column [, column …]]} ON [schema.] table_name [REFERENCING {OLD [AS] old | NEW [AS] new| PARENT as parent}] [FOR EACH ROW ] [WHEN condition] trigger_body;
其中:
l BEFORE和AFTER指出觸發器的觸發時機為前觸發還是後觸發,前觸發是在執行觸發事件之前觸發,後觸發是在執行觸發事件之後觸發當前所創建的觸發器。
l FOR EACH ROW選項說明觸發器為行級觸發器。當省略FOR EACH ROW選項時,BEFORE和AFTER觸發器為語句級觸發器,而INSTEAD OF觸發器則為行級觸發器。
l REFERENCING子句說明相關名稱,在行級觸發器的PL/SQL塊和WHEN子句中可以使用相關名稱參照當前的新、舊列值,默認的相關名稱分別為OLD和NEW。在觸發器的PL/SQL塊中應用相關名稱時,必須在它們之前加冒號,但在WHEN子句中則不能加冒號。
l WHEN子句說明觸發約束條件。當Condition為一個邏輯表達時,其中必須包含相關名稱,而不能包含查詢語句,也不能調用PL/SQL函數。WHEN子句指定的觸發約束條件只能用在BEFORE和AFTER行觸發器中,不能用在INSTEAD OF行觸發器和其它類型的觸發器中。
DML觸發器有如下幾種事件:
1、 INSERTING:當觸發事件是INSERT時,取值為TRUE,否則為FALSE。
2、 UPDATING[(column_1,column_2,…,column_x)]:當觸發事件是UPDATING時,若修改了column_x列,則取值為TRUE,否則為FALSE。其中,column_x是可選的。
3、 DELETING:當觸發事件是DELETE時,則取值為TRUE,否則取值為FALSE。
DML觸發器有如下的限制條件:
l CREATE TRIGGER語句文本的字元長度不能超過32KB
l 觸發器體內的SELECT語句只能為SELECT … INTO … 結構,或者為定義游標所使用的SELECT語句
l 觸發器中不能使用事務控制語句COMMIT、ROLLBACK和SAVEPOINT
l 由觸發器所調用的存儲過程或函數也不能使用資料庫事務控制語句
l 觸發器中不能使用LONG、LONG RAW類型
l 觸發器內不能通過:NEW修改LOB列中的數據
l 觸發器最多可以嵌套32層
當觸發器被觸發時,要使用被插入、更新或刪除的記錄中的列值,有時要使用操作前或操作後列的值,這個時候可以使用:NEW或者:OLD來實現。其中,:NEW表示操作完成後列的值,而:OLD表示操作完成前列的值,如下表所示:
特性 |
INSERT |
UPDATE |
DELETE |
---|---|---|---|
:OLD |
NULL |
修改前的值 |
刪除前的值 |
:NEW |
插入的值 |
修改後的值 |
NULL |
:OLD表和:NEW表是記憶體中的兩個表,其結構和源表結構完全一致。
(1) 當插入時,先將值插入到:NEW表中,在沒有控制的前提下才真正地插入到表中。
(2) 當刪除時,先將要刪除的數據移到:OLD表中,以前的表中的數據就沒有了。
(3) 當更新時,UPDATE XXX SET XXX=:NEW.XXX WHERE XXX=:OLD.XXX,WHERE條件是判斷已有的值,它就移動:OLD表中,SET是設置新的值先放到:NEW表中。
下面舉一個行級觸發器的例子。該觸發器可以實現,當職工表SCOTT.EMP表被刪除一條記錄時,把被刪除記錄寫到職工表刪除日誌表中去,程式碼如下所示:
--建立日誌表 CREATE TABLE SCOTT.EMP_HIS AS SELECT * FROM SCOTT.EMP WHERE 1=2; --建立觸發器 CREATE OR REPLACE TRIGGER SCOTT.TR_DEL_EMP BEFORE DELETE --指定觸發時機為刪除操作前觸發 ON SCOTT.EMP FOR EACH ROW --說明創建的是行級觸發器 BEGIN --將修改前數據插入到日誌記錄表EMP_HIS ,以供監督使用。 INSERT INTO EMP_HIS(DEPTNO , EMPNO, ENAME , JOB ,MGR , SAL , COMM , HIREDATE ) VALUES( :OLD.DEPTNO, :OLD.EMPNO, :OLD.ENAME , :OLD.JOB,:OLD.MGR, :OLD.SAL, :OLD.COMM, :OLD.HIREDATE ); END;
測試行級觸發器:
SYS@lhrdb> SELECT * FROM SCOTT.EMP_HIS; no rows selected SYS@lhrdb> DELETE SCOTT.EMP WHERE EMPNO=7788; 1 row deleted. SYS@lhrdb> SELECT * FROM SCOTT.EMP_HIS; EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO ---------- ---------- --------- ---------- ------------------- ---------- ---------- ---------- 7788 SCOTT ANALYST 7566 1987-04-19 00:00:00 3000 20
下面再舉一個語句級觸發器的例子。
--建立日誌表 CREATE TABLE SCOTT.MYLOG_LHR (CUR_USER VARCHAR2(30), CUR_DATE DATE, ACTION VARCHAR2(30)); --建立觸發器 CREATE OR REPLACE TRIGGER SCOTT.TRI_DML_EMP_LHR AFTER INSERT OR DELETE OR UPDATE ON SCOTT.EMP BEGIN IF INSERTING THEN INSERT INTO SCOTT.MYLOG_LHR (CUR_USER, CUR_DATE, ACTION) VALUES (USER, SYSDATE, 'I'); ELSIF DELETING THEN INSERT INTO SCOTT.MYLOG_LHR VALUES (USER, SYSDATE, 'D'); ELSE INSERT INTO SCOTT.MYLOG_LHR VALUES (USER, SYSDATE, 'U'); END IF; END; /
測試語句級觸發器:
SYS@lhrdb> SELECT * FROM SCOTT.MYLOG_LHR; no rows selected SYS@lhrdb> DELETE SCOTT.EMP WHERE ROWNUM<=1; 1 row deleted. SYS@lhrdb> SELECT * FROM SCOTT.MYLOG_LHR; CUR_USER CUR_DATE ACTION ------------------------------ ------------------- ------------------------------ SYS 2016-10-26 14:14:37 D
2、替代觸發器(INSTEAD OF觸發器)
由於在Oracle里,不能直接對由兩個以上的表建立的視圖進行操作,所以,給出了替代觸發器。可以用替代觸發器解決視圖的多表更新問題,並且替代觸發器只能用於視圖。例如:針對INSERT事件的替代觸發器,它由INSERT語句觸發,當出現INSERT語句時,該語句不會被執行,而是執行替代觸發器中定義的語句。
創建替代觸發器的一般語法如下所示:
CREATE [OR REPLACE] TRIGGER trigger_name INSTEAD OF {INSERT | DELETE | UPDATE [OF column [, column …]]} [OR {INSERT | DELETE | UPDATE [OF column [, column …]]}...] ON [schema.] view_name --只能定義在視圖上 [REFERENCING {OLD [AS] old | NEW [AS] new| PARENT as parent}] [FOR EACH ROW ] --因為INSTEAD OF觸發器只能在行級上觸發,所以沒有必要指定 [WHEN condition] PL/SQL_block | CALL procedure_name;
其中,INSTEAD OF選項使Oracle激活觸發器,而不執行觸發事件。只能對視圖和對象視圖建立INSTEAD OF觸發器,而不能對錶、模式和資料庫建立INSTEAD OF觸發器。其它選項和DML觸發器的語法相同。
創建替代觸發器需要注意以下幾點內容:
① 只能創建在視圖上,並且該視圖沒有指定WITH CHECK OPTION選項。
② 不能指定BEFORE或AFTER選項。
③ FOR EACH ROW是可選的,即替代觸發器只能是行級觸發器,所以,沒有必要指定。
④ 沒有必要在針對一個表的視圖上創建替代觸發器,只要創建DML觸發器就可以了。
⑤ 每一個表和視圖只能有一個替代觸發器。
⑥ 替代觸發器被用於更新那些沒有辦法通過正常方式更新的視圖。
⑦ 替代觸發器的主要優點就是可以使不能更新的視圖支援更新。它支援多個表中數據的插入、更新和刪除操作。
下面舉一個視圖觸發器的例子。首先創建一個含有聚合函數的視圖,如下所示:
CREATE OR REPLACE VIEW SCOTT.VW_EMP_LHR AS SELECT DEPTNO, COUNT(*) TOTAL_EMPLOYEER, SUM(SAL) TOTAL_SALARY FROM SCOTT.EMP GROUP BY DEPTNO;
在此視圖中,當直接刪除時會報錯,如下所示:
SYS@lhrdb> DELETE FROM SCOTT.VW_EMP_LHR WHERE DEPTNO=10; DELETE FROM VW_EMP_LHR WHERE DEPTNO=10 * ERROR at line 1: ORA-01732: data manipulation operation not legal on this view--此視圖的數據操縱操作非法
但是可以通過創建替代觸發器來為DELETE操作執行所需的處理,即刪除EMP表中所有基準行:
CREATE OR REPLACE TRIGGER SCOTT.EMP_VIEW_DELETE
INSTEAD OF DELETE ON SCOTT.VW_EMP_LHR
FOR EACH ROW
BEGIN
DELETE FROM SCOTT.EMP WHERE DEPTNO = :OLD.DEPTNO;
END;
測試替代觸發器,如下所示:
SYS@lhrdb> DELETE FROM SCOTT.VW_EMP_LHR WHERE DEPTNO=10;
1 row deleted.
SYS@lhrdb> SELECT * FROM SCOTT.EMP WHERE DEPTNO=10;
no rows selected
3、DDL觸發器和系統觸發器
由於DDL觸發器和系統觸發器的創建語法很相似,所以,在此一併講解。其實,該部分內容涉及到面試中的一個問題,那就是,「如何監控會話的登錄登出情況?」,答案就是使用審計或系統觸發器來實現。下面將詳細講解該部分的內容。
系統觸發器可以在DDL或資料庫系統上被觸發。DDL指的是數據定義語言,如CREATE、ALTER及DROP等。而資料庫系統事件包括資料庫伺服器的啟動或關閉,用戶的登錄與登出、資料庫服務錯誤等。
值得注意的一點是,隱含參數「_SYSTEM_TRIG_ENABLED」的默認值是TRUE,即允許DDL和系統觸發器。當設置隱含參數「_SYSTEM_TRIG_ENABLED」為FALSE的時候,將禁用DDL和系統觸發器。
創建系統觸發器的一般語法如下所示:
CREATE OR REPLACE TRIGGER [sachema.]trigger_name
{BEFORE|AFTER}
{DDL_EVENT_LIST | DATABASE_EVENT_LIST}
ON { DATABASE | [schema.]SCHEMA }
[WHEN condition]
PL/SQL_block | CALL procedure_name;
其中:
l DDL_EVENT_LIST:一個或多個DDL事件,事件間用OR分開,可以對所有DDL語句監控(直接寫DDL),也可以對個別DDL語句監控。
l DATABASE_EVENT_LIST:一個或多個資料庫事件,事件之間用OR分開。
l ON:系統觸發器按照作用範圍,分為SCHEMA觸發器和DATABASE觸發器。SCHEMA觸發器作用在單個用戶上,而DATABASE觸發器作用在整個資料庫所有用戶上。
l 對於WHEN子句後邊的內容需要由小括弧括起來。
需要注意的是,系統觸發器既可以建立在一個模式上,又可以建立在整個資料庫上。當建立在模式(SCHEMA)之上時,只有模式所指定用戶的DDL操作和該用戶操作所導致的錯誤才能激活觸發器,默認時為當前用戶模式。當建立在資料庫(DATABASE)之上時,該資料庫所有用戶的DDL操作和所有用戶操作所導致的錯誤,以及資料庫的啟動和關閉均可激活觸發器。要在資料庫之上建立觸發器,要求用戶具有ADMINISTER DATABASE TRIGGER許可權或具有DBA角色。一般應該賦予的許可權包括ADMINISTER DATABASE TRIGGER、ALTER ANY TRIGGER和CREATE ANY TRIGGER。
下表給出了系統觸發器的種類和事件出現的時機:
表 3-4系統觸發器的種類和事件
事件 |
允許的時機 |
說明 |
---|---|---|
STARTUP |
AFTER |
啟動資料庫實例之後觸發 |
SHUTDOWN |
BEFORE |
關閉資料庫實例之前觸發(非正常關閉不觸發) |
SERVERERROR |
AFTER |
資料庫伺服器發生錯誤之後觸發。事件SERVERERROR可以用於跟蹤資料庫中發生的錯誤。其錯誤程式碼可以使用觸發器內部的SERVER_ERROR屬性函數取出。該函數可以讓用戶確定堆棧中的錯誤碼。然而,該函數不能返回與該錯誤碼相關的錯誤資訊,但是可以通過使用系統函數DBMS_UTILITY.FORMAT_ERROR_STACK來解決。儘管觸發器本身不會引發錯誤,但藉助於該過程可以使用PL/SQL來訪問錯誤堆棧 |
LOGON |
AFTER |
成功登錄連接到資料庫後觸發 |
LOGOFF |
BEFORE |
開始斷開資料庫連接之前觸發 |
CREATE |
BEFORE、AFTER |
在執行CREATE語句創建資料庫對象之前、之後觸發 |
DROP |
BEFORE、AFTER |
在執行DROP語句刪除資料庫對象之前、之後觸發 |
ALTER |
BEFORE、AFTER |
在執行ALTER語句更新資料庫對象之前、之後觸發 |
DDL |
BEFORE、AFTER |
在執行大多數DDL語句之前、之後觸發 |
GRANT |
BEFORE、AFTER |
執行GRANT語句授予許可權之前、之後觸發 |
REVOKE |
BEFORE、AFTER |
執行REVOKE語句收許可權之前、之後觸犯發 |
RENAME |
BEFORE、AFTER |
執行RENAME語句更改資料庫對象名稱之前、之後觸犯發 |
AUDIT/NOAUDIT |
BEFORE、AFTER |
執行AUDIT或NOAUDIT進行審計或停止審計之前、之後觸發 |
除DML語句的列屬性外,其餘事件屬性值可通過調用Oracle定義的事件屬性函數來讀取,參考下表:
表 3-5事件屬性函數
函數名稱 |
同義詞 |
數據類型 |
說明 |
---|---|---|---|
SYSEVENT |
ORA_SYSEVENT |
字元串 |
激活觸發器的事件名稱 |
INSTANCE_NUM |
ORA_INSTANCE_NUM |
數值 |
資料庫實例號 |
DATABASE_NAME |
ORA_DATABASE_NAME |
字元串 |
資料庫名稱 |
SERVER_ERROR(POSI) |
ORA_SERVER_ERROR |
數值 |
錯誤資訊棧中POSI指定位置中的錯誤號 |
IS_SERVERERROR(ERR_NUMBER) |
ORA_IS_SERVERERROR |
布爾值 |
檢查ERR_NUMBER指定的錯誤號是否在錯誤資訊棧中,若在則返回TRUE,否則返回FALSE。在觸發器內調用此函數可以判斷是否發生指定的錯誤 |
LOGIN_USER |
ORA_LOGIN_USER |
字元串 |
登陸或註銷的用戶名稱 |
DICTIONARY_OBJ_TYPE |
ORA_DICT_OBJ_TYPE |
字元串 |
DDL語句所操作的資料庫對象類型 |
DICTIONARY_OBJ_NAME |
ORA_DICT_OBJ_NAME |
字元串 |
DDL語句所操作的資料庫對象名稱 |
DICTIONARY_OBJ_OWNER |
ORA_DICT_OBJ_OWNER |
字元串 |
DDL語句所操作的資料庫對象所有者名稱 |
DES_ENCRYPTED_PASSWORD |
ORA_DES_ENCRYPTED_PASSWORD |
字元串 |
正在創建或修改的經過DES演算法加密的用戶口令 |
CLIENT_IP_ADDRESS |
ORA_CLIENT_IP_ADDRESS |
字元串 |
用於返回客戶端IP地址 |
上表中的函數和同義詞的對應關係可以通過如下的SQL語句找到:
SELECT * FROM DBA_SYNONYMS D WHERE D.SYNONYM_NAME LIKE 'ORA%';
就像DML觸發器一樣,系統觸發器可以使用WHEN子句來指定觸發器激活條件。關於系統觸發器需要了解以下幾點:
l STARTUP和SHUTDOWN觸發器不能帶有任何條件。
l SERVERERROR觸發器可以使用ERRNO測試來檢查特定的錯誤。
l LOGON和LOGOFF觸發器可以使用USERID或USERNAME測試來檢查用戶標識或用戶名。
l DDL觸發器可以檢查正在修改對象的名稱、類型和操作類別。
DDL觸發器有很多實際用途,如下所示:
① 建表的同時建立公共同義詞
② 阻止非授權用戶的TRUNCATE操作
③ 記錄所有的DDL語句(包括SERVERERROR、GRANT、SHUTDDOWN、ALTER、REVOKE、DROP、TRUNCATE、COMMENT、STARTUP、AUDIT、CREATE、ANALYZE),以便查找責任人
④ 阻止DDL操作
⑤ 記錄伺服器錯誤
⑥ 填充V$SESSION的CLIENT_INFO和CLIENT_IDENTIFIER列
⑦ 記錄用戶登錄資料庫失敗的詳細資訊
⑧ 監控會話的登錄登出情況
要禁用或啟用表的所有觸發器,可以使用ALTER TABLE語句,如下所示:
ALTER TABLE T_20161026_LHR DISABLE ALL TRIGGERS; –禁用觸發器
ALTER TABLE T_20161026_LHR ENABLE ALL TRIGGERS; –啟用觸發器
將觸發器設置為禁用或啟用使用ALTER TRIGGER語句,如下所示:
ALTER TRIGGER TRIGGER_NAME ENABLE;
ALTER TRIGGER TRIGGER_NAME DISABLE;
重新編譯觸發器的語句為:
ALTER TRIGGER [SCHEMA.] TRIGGER_NAME COMPILE;
最後,介紹一下編寫觸發器的一些注意事項:
l 觸發器不接受參數。
l 一個表上最多可以有12個觸發器,但同一時間、同一事件、同一類型的觸發器只能有一個。
l 在一個表上的觸發器越多,對在該表上的DML操作的性能影響就越大。
l 觸發器最大為32KB。若確實需要,則可以先建立存儲過程,然後在觸發器中調用存儲過程。
l 在觸發器的執行部分只能使用DML語句(例如SELECT、INSERT、UPDATE、DELETE等),不能使用DDL語句(例如CREATE、ALTER、DROP等)。
l 觸發器中不能包含事務控制語句(例如COMMIT、ROLLBACK、SAVEPOINT等)。因為觸發器是觸發語句的一部分,當觸發語句被提交、回退時,觸發器也被提交、回退了。
l 在觸發器主體中調用的任何存儲過程、函數,都不能使用事務控制語句。
l 在觸發器主體中不能聲明任何LONG或BLOB變數。
About Me:小麥苗
● 本文作者:小麥苗,只專註於資料庫的技術,更注重技術的運用
● 作者部落格地址:http://blog.itpub.net/26736162/abstract/1/
● 本系列題目來源於作者的學習筆記,部分整理自網路,若有侵權或不當之處還請諒解
● 版權所有,歡迎分享本文,轉載請保留出處
● 題目解答若有不當之處,還望各位朋友批評指正,共同進步