Oracle數據庫之PL/SQL觸發器


1. 介紹

觸發器(trigger)是數據庫提供給程序員和數據分析員來保證數據完整性的一種方法,它是與表事件相關的特殊的存儲過程,它的執行不是由程序調用,也不是手工啟動,而是由事件來觸發,比如當對一個表進行操作(insert,delete,update)時就會激活它執行。觸發器經常用於加強數據的完整性約束和業務規則等。

ORACLE觸發器有三種類型,分別是:DML觸發器、替代觸發器和系統觸發器。

DML觸發器

顧名思義,DML觸發器是由DML語句觸發的。例如數據庫的INSERT、UPDATE、DELETE操作都可以觸發該類型的觸發器。它們可以在這些語句之前或之后觸發,或者在行級上觸發(就是說對於每個受影響的行都觸發一次)。

替代觸發器

替代觸發器只能使用在視圖上,與DML不同的是,DML觸發器是運行在DML之外的,而替代觸發器是代替激發它的DML語句運行。替代觸發器是行觸發器。

系統觸發器

這種觸發器是發生在如數據庫啟動或關閉等系統事件時,不是在執行DML語句時發生,當然也可以在DDL時觸發。

觸發器功能強大,輕松可靠地實現許多復雜的功能,但是我們也應該慎用。為什么又要慎用呢?觸發器本身沒有過錯,但如果我們濫用,會造成數據庫及應用程序的維護困難。在數據庫操作中,我們可以通過關系、觸發器、存儲過程、應用程序等來實現數據操作,同時約束、缺省值也是保證數據完整性的重要保障。如果我們對觸發器過分的依賴,勢必影響數據庫的結構,同時增加了維護的復雜程度。

2. 觸發器組成

觸發器主要由以下幾個要素組成:

  1. 觸發事件:引起觸發器被觸發的事件。
  2. 觸發時間:觸發器是在觸發事件發生之前(BEFORE)還是之后(AFTER)觸發,也就是觸發事件和該觸發器的操作順序。
  3. 觸發操作:觸發器被觸發之后的目的和意圖,是觸發器本身要做的事情。
  4. 觸發對象:包括表、視圖、模式、數據庫。只有在這些對象上發生了符合觸發條件的觸發事件,才會執行觸發操作。
  5. 觸發條件:由WHEN子句指定一個邏輯表達式。只有當該表達式的值為TRUE時,遇到觸發事件才會自動執行觸發器,使其執行觸發操作。
  6. 觸發頻率:說明觸發器內定義的動作被執行的頻率。即語句級(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提供的條件謂詞:

  1. INSERTING:當觸發事件是INSERT時,取值為TRUE,否則為FALSE。
  2. UPDATING [(column_1,column_2,…,column_x)]:當觸發事件是UPDATE時,如果修改了column_x列,則取值為TRUE,否則為FALSE。
  3. 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觸發器需要注意以下幾點:

  1. 只能被創建在視圖上,並且該視圖沒有指定WITH CHECK OPTION選項。
  2. 不能指定BEFORE或AFTER選項。
  3. FOR EACH ROW子句是可選的。
  4. 沒有必要在針對一個表的視圖上創建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進行審計或停止審計之前、之后觸發

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM