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邏輯組合)。ide
② 觸發時間(觸發時機):即該TRIGGER是在觸發事件發生以前(BEFORE)仍是以後(AFTER)觸發,也就是觸發事件和該TRIGGER的操做順序。若是指定爲BEFORE,那麼表示在執行DML操做以前觸發,以便防止某些錯誤操做發生或實現某些業務規則;若是指定爲AFTER,那麼表示在執行DML操做以後觸發,以便記錄該操做或作某些過後處理。函數
③ 觸發器自己:即該TRIGGER被觸發以後的目的和意圖,正是觸發器自己要作的事情。例如:PL/SQL塊。性能
④ 觸發頻率:說明觸發器內定義的動做被執行的次數,分爲語句級(STATEMENT)觸發器和行級(ROW)觸發器。測試
a. 語句級(STATEMENT)觸發器:是指當某觸發事件發生時,該觸發器只執行一次;語句級觸發器不容許和WHEN子句一塊兒使用。this
b. 行級(ROW)觸發器:是指當某觸發事件發生時,對受到該操做影響的每一行數據,觸發器都單獨執行一次。加密
c. 當某操做隻影響到表中的一行數據時,語句級觸發器與行級觸發器的效果相同。
換句話說,語句級觸發器針對某一條語句只觸發一次,而行級觸發器則針對語句所影響的每一行都觸發一次。例如:某條UPDATE語句修改了表中的100行數據,那麼針對該UPDATE事件的語句級觸發器將被觸發一次,而行級觸發器將被觸發100次。
⑤ 觸發對象:包括表、視圖、用戶、數據庫。只有在這些對象上發生了符合觸發條件的觸發事件時,纔會執行觸發操做。
⑥ 觸發條件:由WHEN子句指定一個邏輯表達式。只有當該表達式的值爲TRUE時,遇到觸發事件纔會自動執行觸發器,使其執行觸發操做。
下面分別介紹這4類觸發器。
一、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觸發器有以下幾種事件:
一、 INSERTING:當觸發事件是INSERT時,取值爲TRUE,不然爲FALSE。
二、 UPDATING[(column_1,column_2,…,column_x)]:當觸發事件是UPDATING時,若修改了column_x列,則取值爲TRUE,不然爲FALSE。其中,column_x是可選的。
三、 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
二、替代觸發器(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
三、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變量。