ORACLE PL/SQL 觸發器(trigger)學習筆記(1)

1、觸發器的概念

觸發器也是一種帶名的PL/SQL塊。觸發器類似于過程和函數,因為它們都是擁有聲明、執行和異常處理過程的帶名PL/SQL塊。與包類似,觸發器必須存儲在數據庫中并且不能被塊進行本地化聲明。

對于觸發器而言,當觸發事件發生的時候就會顯式地執行該觸發器,并且觸發器不接受參數。

 

創建觸發器的語法如下

 

view plaincopy to clipboardprint?

CREATE [OR REPLACE] TRIGGER trigger_name

{BEFORE | AFTER | INSTEAD OF} triggering_event

[referencing_clause]

[WHEN trigger_condition]

[FOR EACH ROW]

Trigger_body;

CREATE [OR REPLACE] TRIGGER trigger_name

{BEFORE | AFTER | INSTEAD OF} triggering_event

[referencing_clause]

[WHEN trigger_condition]

[FOR EACH ROW]

Trigger_body;

其中referencing_clause子句的用途是通過一個不同的名稱,引用當前正在被更新的記錄行中的數據。WHEN子句中的trigger_condition—如果出現—就應該首先執行判斷,只有當這個條件值為真的時候,才會執行觸發器的主體代碼。

 

 

2、DML觸發器的激活順序

1)執行before語句級觸發器—如果存在這種觸發器

2)對受該語句影響的每一行記錄

執行before行級觸發器—如果存在這種觸發器

執行該語句本身

執行after行級觸發器--如果存在這種觸發器

3)執行after語句級觸發器--如果存在這種觸發器

 

同一種類型的觸發器的點火次序沒有經過定義。如果該次序很重要的話,那么建議將所有這些操作組合到一個觸發器當中。

 

3、行級觸發器中的關聯標識符

觸發器的激活語句每處理一行數據,行級觸發器就會激活一次。可以在這種行級觸發器內部,訪問正被處理的記錄行中的數據。這是通過兩個關聯標識符--:old和:new—實現的。關聯標識符也是PL/SQL的一種特殊的綁定變量。標識符前面的冒號,既說明這二者都是綁定變量,同時也說明它們不是一般的PL/SQL變量。PL/SQL編譯器會將它們看作下面這個類型的記錄:

 

Triggering_table%ROWTYPE

 

其中triggering_table是在其上定義觸發器的表名。于是,下面這種引用

 

:new.field

 

就只有當其中的field是該觸發表中的字段名時才會有效。

 

觸發語句

:old

:new

INSERT

未定義—所有字段均為NULL

觸發語句完成的時候,要插入的值

UPDATE

更新以前相應記錄行的原始值

觸發語句完成的時候,要更新的值

DELETE

刪除以前相應記錄行的原始值

未定義—所有字段均為NULL

 

注意:INSERT語句上沒有定義:old標識符,DELETE語句上也沒有定義:new標識符。如果再INSERT語句上使用:old標識符,或者在DELETE語句上使用:new標識符,PL/SQL并不會產生錯誤,但是這兩個字段值都會為NULL。

 

偽記錄

雖然在語法構成上,會將:new和:old看作triggering_table%ROWTYPE類型的記錄,但是,實際上它們并不是記錄。因此,那些能夠在記錄上正常執行的操作,并不能在:new和:old上執行。例如,不能將它們作為一個整體進行賦值。只能對其中的各個字段分別賦值。

 

 

view plaincopy to clipboardprint?

CREATE OR REPLACE TRIGGER TempDelete

BEFORE DELETE ON temp_table

FOR EACH ROW

DECLARE

v_TempRec temp_table%ROWTYPE;

BEGIN

/* This is not a legal assignment, since :old is not truly

a record. */

v_TempRec := :old;

/* We can accomplish the same thing, however, by assigning

the fields individually. */

v_TempRec.char_col := :old.char_col;

v_TempRec.num_col := :old.num_col;

END TempDelete;

/

CREATE OR REPLACE TRIGGER TempDelete

BEFORE DELETE ON temp_table

FOR EACH ROW

DECLARE

v_TempRec temp_table%ROWTYPE;

BEGIN

/* This is not a legal assignment, since :old is not truly

a record. */

v_TempRec := :old;

 

/* We can accomplish the same thing, however, by assigning

the fields individually. */

v_TempRec.char_col := :old.char_col;

v_TempRec.num_col := :old.num_col;

END TempDelete;

/

 

REFERENCING子句

還可以使用REFERENCING子句,為:old和:new換一個不同的名稱。該子句出現在觸發事件以后,WHEN子句以前。其語法如下:

 

REFERENCING [OLD AS old_name] [NEW AS new_name]

 

在觸發器主體中,可以使用:old_name和:new_name分別代替:old和:new。

 

注意,在REFERENCING子句中關聯標識符都不帶冒號。

 

如下面這個例子所示

view plaincopy to clipboardprint?

CREATE OR REPLACE TRIGGER GenerateAuthorID

BEFORE INSERT OR UPDATE ON authors

REFERENCING new AS new_author

FOR EACH ROW

BEGIN

/* Fill in the ID field of authors with the next value from

author_sequence. Since ID is a column in authors, :new.ID

is a valid reference. */

SELECT author_sequence.NEXTVAL

INTO :new_author.ID

FROM dual;

END GenerateAuthorID;

/

CREATE OR REPLACE TRIGGER GenerateAuthorID

BEFORE INSERT OR UPDATE ON authors

REFERENCING new AS new_author

FOR EACH ROW

BEGIN

/* Fill in the ID field of authors with the next value from

author_sequence. Since ID is a column in authors, :new.ID

is a valid reference. */

SELECT author_sequence.NEXTVAL

INTO :new_author.ID

FROM dual;

END GenerateAuthorID;

/

 

4、WHEN子句

WHEN子句只能在行級觸發器中使用。如果在行級觸發器的定義中給出了WHEN子句,觸發器主體就只對滿足WHEN所定義條件的那些記錄行執行。WHEN子句的基本形式如下:

 

WHEN trigger_condition

 

其中,trigger_condition是一個布爾表達式。每處理一行記錄,都會重新判斷該表達式的值。

 

也可以在trigger_condition內部使用:new和:old記錄,但是與REFERENCING子句一樣,在trigger_condition內部使用:new和:old時,不需要冒號。僅在觸發器主體中才需要使用冒號。

 

 

5、觸發器謂詞

可以在觸發器內部使用3個布爾函數,判斷觸發該觸發器的到底是什么操作。這3個謂詞分別是INSERTING、UPDATING和DELETING.

使用方法如下面這個例子所示

 

view plaincopy to clipboardprint?

CREATE OR REPLACE TRIGGER LogInventoryChanges

BEFORE INSERT OR DELETE OR UPDATE ON inventory

FOR EACH ROW

DECLARE

v_ChangeType CHAR(1);

BEGIN

/* Use "I" for an INSERT, "D" for DELETE, and "U" for UPDATE. */

IF INSERTING THEN

v_ChangeType := "I";

ELSIF UPDATING THEN

v_ChangeType := "U";

ELSE

v_ChangeType := "D";

END IF;

/* Record all the changes made to inventory in

inventory_audit. Use SYSDATE to generate the timestamp, and

USER to return the userid of the current user. */

INSERT INTO inventory_audit

(change_type, changed_by, timestamp,

old_isbn, old_status, old_status_date, old_amount,

new_isbn, new_status, new_status_date, new_amount)

VALUES

(v_ChangeType, USER, SYSDATE,

:old.isbn, :old.status, :old.status_date, :old.amount,

:new.isbn, :new.status, :new.status_date, :new.amount);

END LogInventoryChanges;

/

CREATE OR REPLACE TRIGGER LogInventoryChanges

BEFORE INSERT OR DELETE OR UPDATE ON inventory

FOR EACH ROW

DECLARE

v_ChangeType CHAR(1);

BEGIN

/* Use "I" for an INSERT, "D" for DELETE, and "U" for UPDATE. */

IF INSERTING THEN

v_ChangeType := "I";

ELSIF UPDATING THEN

v_ChangeType := "U";

ELSE

v_ChangeType := "D";

END IF;

 

/* Record all the changes made to inventory in

inventory_audit. Use SYSDATE to generate the timestamp, and

USER to return the userid of the current user. */

INSERT INTO inventory_audit

(change_type, changed_by, timestamp,

old_isbn, old_status, old_status_date, old_amount,

new_isbn, new_status, new_status_date, new_amount)

VALUES

(v_ChangeType, USER, SYSDATE,

:old.isbn, :old.status, :old.status_date, :old.amount,

:new.isbn, :new.status, :new.status_date, :new.amount);

END LogInventoryChanges;

/

 

 

6、INSTEAD-OF觸發器

INSTEAD-OF觸發器僅可以定義在視圖上(關系型的或對象),并且它們可以替代點火它們的DML語句進行點火。INSTEAD-OF觸發器必須是行級的。

 

 

7、觸發器的限制

觸發器的主體是一個PL/SQL塊。在PL/SQL塊中可以使用的所有語句在觸發器主體中都是合法的,但是要受到下面限制的約束:

 

觸發器不應該使用事務控制語句—COMMIT、ROLLBACK或SAVEPOINT。觸發器作為觸發語句執行的一部分被點火,它和觸發語句在同一個事務中。當觸發語句被提交或撤回提交時,觸發器的工作也相應被提交會撤回提交。

由觸發器主體調用的任何過程和函數都不能使用事務控制語句。

觸發器主體不能聲明任何LONG或者LONG RAW變量。而且,:new和:old不能指向定義觸發器的表中的LONG和LONG RAW列。

觸發器主體可以訪問的表有所限制。

觸發器P-Code

當包或者子程序存儲在數據字典中時,存儲的除了該對象的源代碼還有經過編譯的p-code。但是對于觸發器來說就不是這樣的。在數據字典中唯一存儲的是觸發器的源代碼,而不是p-code。結果,每次當從數據字典中重新讀出觸發器時,必須要進行編譯。這對觸發器的定義和使用的方式不會帶來什么影響,但是會影響觸發器的性能。

 

 

8、系統觸發器

我們前面所看到的DML觸發器和INSTEAD-OF觸發器都是基于DML事件。而另一方面,系統觸發器的激活則是基于兩種不同的事件:DDL事件或數據庫事件。DDL事件包括CREATE、ALTER或DROP語句,而數據庫事件包括數據庫服務器的啟動/關閉事件,用戶的登陸/斷開事件,以及服務器錯誤。創建系統觸發器的語法如下:

 

view plaincopy to clipboardprint?

CREATE [OR REPLACE] TRIGGER [schema.]trigger_name

{BEFORE | AFTER}

{ddl_event_list | database_event_list}

ON {DATABASE | [schema.]SCHEMA}

[when_clause]

Trigger_body;

CREATE [OR REPLACE] TRIGGER [schema.]trigger_name

{BEFORE | AFTER}

{ddl_event_list | database_event_list}

ON {DATABASE | [schema.]SCHEMA}

[when_clause]

Trigger_body;

 

其中,ddl_event_list是由OR關鍵字隔開的一個或多個DDL事件,database_event_list則是由OR關鍵字隔開的一個或多個數據庫事件。

 

注意:不能創建INSTEAD-OF系統級觸發器。

 

通過子句ON {DATABASE | [schema.]SCHEMA}我們可以指定這個系統觸發器是定義在數據庫級上還是模式級上。只要發生了激活事件,數據庫級觸發器就會激活。而只有激活事件發生在某個具體模式中,相應的模式級觸發器才會激活。如果使用SCHEMA關鍵字的時候沒有定義某個具體模式的名稱,那么默認設置為擁有這個觸發器的模式。

 

 

9、修改觸發器狀態和刪除觸發器

 

啟動或禁用觸發器

 

ALTER TRIGGER trigger_name {DISABLE | ENABLE};

 

刪除觸發器

 

DROP TRIGGER trigger_name;

 

還可以使用ALTER TABLE命令,并附加使用ENABLE ALL TRIGGERS或DISABLE ALL TRIGGERS子句,同時將某一個表上的所有觸發器開啟或關閉。

 

ALTER TABLE table_name {ENABLE | DISABLE} ALL TRIGGERS;

 

可以通過user_triggers來查看相應觸發器信息。

 

 

10、變化表和限制表

觸發器主體(trigger body)可以訪問的表和列上有一些限制。在定義這些限制以前,我們先看兩個概念—變化表和限制表。

“變化表”(mutating table)是被DML語句正在修改的表。對于觸發器而言,它就是定義觸發器的表。需要作為DELETE CASCADE參考完整性限制(referential integrity constraints)的結果進行更新的表也是變化的(mutating)。

“限制表”(constraining table)是可能需要對參考完整性限制執行讀操作的表。

 

為了更好的理解定義,我們看下面這個例子

 

view plaincopy to clipboardprint?

CREATE TABLE registered_students (

student_id NUMBER(5) NOT NULL,

department CHAR(3)   NOT NULL,

course     NUMBER(3) NOT NULL,

grade      CHAR(1),

CONSTRAINT rs_grade

CHECK (grade IN ("A", "B", "C", "D", "E")),

CONSTRAINT rs_student_id

FOREIGN KEY (student_id) REFERENCES students (id),

CONSTRAINT rs_department_course

FOREIGN KEY (department, course)

REFERENCES classes (department, course)

);

--...

CREATE TABLE registered_students (

student_id NUMBER(5) NOT NULL,

department CHAR(3)   NOT NULL,

course     NUMBER(3) NOT NULL,

grade      CHAR(1),

CONSTRAINT rs_grade

CHECK (grade IN ("A", "B", "C", "D", "E")),

CONSTRAINT rs_student_id

FOREIGN KEY (student_id) REFERENCES students (id),

CONSTRAINT rs_department_course

FOREIGN KEY (department, course)

REFERENCES classes (department, course)

);

--...

 

registered_students有兩個聲明的參考完整性限制。

Students和classes都是registered_students的限制表。

 

觸發器主體中的SQL不允許進行:

讀取或修改觸發語句(triggering statement)的任何變化表。這些表包括觸發表(triggering table )自己。

讀取或修改觸發表(triggering table)的限制表中的主鍵(primary)、唯一列值(unique)或外鍵(foreign key)列。但是如果需要的話,可以修改其他列。

轉載:http://blog.csdn.net/wh62592855/archive/2009/10/27/4735701.aspx

免責聲明:本文僅代表文章作者的個人觀點,與本站無關。其原創性、真實性以及文中陳述文字和內容未經本站證實,對本文以及其中全部或者部分內容文字的真實性、完整性和原創性本站不作任何保證或承諾,請讀者僅作參考,并自行核實相關內容。

http://www.uswqb.club/style/images/nopic.gif
分享
評論
首頁
高速公路之王电子游艺
qq分分彩开奖结果 今晚南粤风彩36选 浙江快乐彩12选5走势图 免费打麻将游戏下载 浙江20选5号码走势图 法乙梅斯今天的比赛比分 苹果怎么下载科乐长春麻将 广西快乐双彩开奖彩控网 北京麻将牌去哪里买 内蒙古11选5购买 彩票天天选四开奖结果 小程序微乐陕西麻将辅助器 体彩超级大乐透开奖直播 怎么下载全民福州麻将 广东11选5开奖结果走势图 体彩顶呱刮害人