2019-03-21 oracle 出發(fā)器詳解

Oracle觸發(fā)器詳解

轉(zhuǎn)載自http://blog.csdn.net/indexman/article/details/8023740/


觸發(fā)器是許多關(guān)系數(shù)據(jù)庫(kù)系統(tǒng)都提供的一項(xiàng)技術(shù)。在oracle系統(tǒng)里财破,觸發(fā)器類似過(guò)程和函數(shù)烦秩,都有聲明,執(zhí)行和異常處理過(guò)程的PL/SQL塊尊搬。

8.1?觸發(fā)器類型

??? 觸發(fā)器在數(shù)據(jù)庫(kù)里以獨(dú)立的對(duì)象存儲(chǔ)扛或,它與存儲(chǔ)過(guò)程和函數(shù)不同的是洲劣,存儲(chǔ)過(guò)程與函數(shù)需要用戶顯示調(diào)用才執(zhí)行,而觸發(fā)器是由一個(gè)事件來(lái)啟動(dòng)運(yùn)行空幻。即觸發(fā)器是當(dāng)某個(gè)事件發(fā)生時(shí)自動(dòng)地隱式運(yùn)行烁峭。并且,觸發(fā)器不能接收參數(shù)秕铛。所以運(yùn)行觸發(fā)器就叫觸發(fā)或點(diǎn)火(firing)约郁。ORACLE事件指的是對(duì)數(shù)據(jù)庫(kù)的表進(jìn)行的INSERT缩挑、UPDATE及DELETE操作或?qū)σ晥D進(jìn)行類似的操作。ORACLE將觸發(fā)器的功能擴(kuò)展到了觸發(fā)ORACLE棍现,如數(shù)據(jù)庫(kù)的啟動(dòng)與關(guān)閉等调煎。所以觸發(fā)器常用來(lái)完成由數(shù)據(jù)庫(kù)的完整性約束難以完成的復(fù)雜業(yè)務(wù)規(guī)則的約束,或用來(lái)監(jiān)視對(duì)數(shù)據(jù)庫(kù)的各種操作己肮,實(shí)現(xiàn)審計(jì)的功能士袄。


8.1.1 DML觸發(fā)器

??? ORACLE可以在DML語(yǔ)句進(jìn)行觸發(fā),可以在DML操作前或操作后進(jìn)行觸發(fā)谎僻,并且可以對(duì)每個(gè)行或語(yǔ)句操作上進(jìn)行觸發(fā)娄柳。


8.1.2 替代觸發(fā)器

??? 由于在ORACLE里,不能直接對(duì)由兩個(gè)以上的表建立的視圖進(jìn)行操作艘绍。所以給出了替代觸發(fā)器赤拒。它就是ORACLE 8專門(mén)為進(jìn)行視圖操作的一種處理方法。


8.1.3 系統(tǒng)觸發(fā)器

ORACLE 8i 提供了第三種類型的觸發(fā)器叫系統(tǒng)觸發(fā)器诱鞠。它可以在ORACLE數(shù)據(jù)庫(kù)系統(tǒng)的事件中進(jìn)行觸發(fā)挎挖,如ORACLE系統(tǒng)的啟動(dòng)與關(guān)閉等。


觸發(fā)器組成:?

l?????????觸發(fā)事件:引起觸發(fā)器被觸發(fā)的事件航夺。 例如:DML語(yǔ)句(INSERT, UPDATE, DELETE語(yǔ)句對(duì)表或視圖執(zhí)行數(shù)據(jù)處理操作)蕉朵、DDL語(yǔ)句(如CREATE、ALTER阳掐、DROP語(yǔ)句在數(shù)據(jù)庫(kù)中創(chuàng)建始衅、修改、刪除模式對(duì)象)缭保、數(shù)據(jù)庫(kù)系統(tǒng)事件(如系統(tǒng)啟動(dòng)或退出汛闸、異常錯(cuò)誤)、用戶事件(如登錄或退出數(shù)據(jù)庫(kù))艺骂。

l?????????觸發(fā)時(shí)間:即該TRIGGER 是在觸發(fā)事件發(fā)生之前(BEFORE)還是之后(AFTER)觸發(fā)诸老,也就是觸發(fā)事件和該TRIGGER 的操作順序。

l?????????觸發(fā)操作:即該TRIGGER 被觸發(fā)之后的目的和意圖彻亲,正是觸發(fā)器本身要做的事情孕锄。 例如:PL/SQL 塊。

l?????????觸發(fā)對(duì)象:包括表苞尝、視圖、模式宦芦、數(shù)據(jù)庫(kù)宙址。只有在這些對(duì)象上發(fā)生了符合觸發(fā)條件的觸發(fā)事件,才會(huì)執(zhí)行觸發(fā)操作调卑。

l?????????觸發(fā)條件:由WHEN子句指定一個(gè)邏輯表達(dá)式抡砂。只有當(dāng)該表達(dá)式的值為T(mén)RUE時(shí)大咱,遇到觸發(fā)事件才會(huì)自動(dòng)執(zhí)行觸發(fā)器,使其執(zhí)行觸發(fā)操作注益。

l?????????觸發(fā)頻率:說(shuō)明觸發(fā)器內(nèi)定義的動(dòng)作被執(zhí)行的次數(shù)碴巾。即語(yǔ)句級(jí)(STATEMENT)觸發(fā)器和行級(jí)(ROW)觸發(fā)器。

語(yǔ)句級(jí)(STATEMENT)觸發(fā)器:是指當(dāng)某觸發(fā)事件發(fā)生時(shí)丑搔,該觸發(fā)器只執(zhí)行一次厦瓢;

行級(jí)(ROW)觸發(fā)器:是指當(dāng)某觸發(fā)事件發(fā)生時(shí),對(duì)受到該操作影響的每一行數(shù)據(jù)啤月,觸發(fā)器都單獨(dú)執(zhí)行一次煮仇。

編寫(xiě)觸發(fā)器時(shí),需要注意以下幾點(diǎn):

l???????? 觸發(fā)器不接受參數(shù)谎仲。

l???????? 一個(gè)表上最多可有12個(gè)觸發(fā)器浙垫,但同一時(shí)間、同一事件郑诺、同一類型的觸發(fā)器只能有一個(gè)夹姥。并各觸發(fā)器之間不能有矛盾。

l???????? 在一個(gè)表上的觸發(fā)器越多辙诞,對(duì)在該表上的DML操作的性能影響就越大辙售。

l????????觸發(fā)器最大為32KB。若確實(shí)需要倘要,可以先建立過(guò)程圾亏,然后在觸發(fā)器中用CALL語(yǔ)句進(jìn)行調(diào)用。

l在觸發(fā)器的執(zhí)行部分只能用DML語(yǔ)句(SELECT封拧、INSERT志鹃、UPDATE、DELETE)泽西,不能使用DDL語(yǔ)句(CREATE曹铃、ALTER、DROP)捧杉。

l???????? 觸發(fā)器中不能包含事務(wù)控制語(yǔ)句(COMMIT陕见,ROLLBACK,SAVEPOINT)味抖。因?yàn)橛|發(fā)器是觸發(fā)語(yǔ)句的一部分评甜,觸發(fā)語(yǔ)句被提交、回退時(shí)仔涩,觸發(fā)器也被提交忍坷、回退了。

l???????? 在觸發(fā)器主體中調(diào)用的任何過(guò)程、函數(shù)佩研,都不能使用事務(wù)控制語(yǔ)句柑肴。

l???????? 在觸發(fā)器主體中不能申明任何Long和blob變量。新值new和舊值old也不能是表中的任何long和blob列旬薯。

l???????? 不同類型的觸發(fā)器(如DML觸發(fā)器晰骑、INSTEAD OF觸發(fā)器、系統(tǒng)觸發(fā)器)的語(yǔ)法格式和作用有較大區(qū)別绊序。

8.2?創(chuàng)建觸發(fā)器

創(chuàng)建觸發(fā)器的一般語(yǔ)法是:



CREATE?[OR?REPLACE]?TRIGGER?trigger_name

{BEFORE?|?AFTER?}

{INSERT?|?DELETE?|?UPDATE?[OF?column?[,?column?…]]}

[OR?{INSERT?|?DELETE?|?UPDATE?[OF?column?[,?column?…]]}...]

ON?[schema.]table_name?|?[schema.]view_name

[REFERENCING?{OLD?[AS]?old?|?NEW?[AS]?new|?PARENT?as?parent}]

[FOR?EACH?ROW?]

[WHEN?condition]

PL/SQL_BLOCK?|?CALL?procedure_name;




其中:

BEFORE 和AFTER指出觸發(fā)器的觸發(fā)時(shí)序分別為前觸發(fā)和后觸發(fā)方式硕舆,前觸發(fā)是在執(zhí)行觸發(fā)事件之前觸發(fā)當(dāng)前所創(chuàng)建的觸發(fā)器,后觸發(fā)是在執(zhí)行觸發(fā)事件之后觸發(fā)當(dāng)前所創(chuàng)建的觸發(fā)器政模。

?????? FOR EACH ROW選項(xiàng)說(shuō)明觸發(fā)器為行觸發(fā)器岗宣。行觸發(fā)器和語(yǔ)句觸發(fā)器的區(qū)別表現(xiàn)在:行觸發(fā)器要求當(dāng)一個(gè)DML語(yǔ)句操作影響數(shù)據(jù)庫(kù)中的多行數(shù)據(jù)時(shí),對(duì)于其中的每個(gè)數(shù)據(jù)行淋样,只要它們符合觸發(fā)約束條件耗式,均激活一次觸發(fā)器;而語(yǔ)句觸發(fā)器將整個(gè)語(yǔ)句操作作為觸發(fā)事件趁猴,當(dāng)它符合約束條件時(shí)刊咳,激活一次觸發(fā)器。當(dāng)省略FOR EACH ROW 選項(xiàng)時(shí)儡司,BEFORE 和AFTER 觸發(fā)器為語(yǔ)句觸發(fā)器娱挨,而INSTEAD OF?觸發(fā)器則只能為行觸發(fā)器。

???????????REFERENCING 子句說(shuō)明相關(guān)名稱捕犬,在行觸發(fā)器的PL/SQL塊和WHEN 子句中可以使用相關(guān)名稱參照當(dāng)前的新跷坝、舊列值,默認(rèn)的相關(guān)名稱分別為OLD和NEW碉碉。觸發(fā)器的PL/SQL塊中應(yīng)用相關(guān)名稱時(shí)柴钻,必須在它們之前加冒號(hào)(:),但在WHEN子句中則不能加冒號(hào)垢粮。

WHEN 子句說(shuō)明觸發(fā)約束條件贴届。Condition 為一個(gè)邏輯表達(dá)時(shí),其中必須包含相關(guān)名稱蜡吧,而不能包含查詢語(yǔ)句毫蚓,也不能調(diào)用PL/SQL 函數(shù)。WHEN 子句指定的觸發(fā)約束條件只能用在BEFORE 和AFTER 行觸發(fā)器中昔善,不能用在INSTEAD OF 行觸發(fā)器和其它類型的觸發(fā)器中元潘。

??? 當(dāng)一個(gè)基表被修改( INSERT, UPDATE, DELETE)時(shí)要執(zhí)行的存儲(chǔ)過(guò)程,執(zhí)行時(shí)根據(jù)其所依附的基表改動(dòng)而自動(dòng)觸發(fā)君仆,因此與應(yīng)用程序無(wú)關(guān)柬批,用數(shù)據(jù)庫(kù)觸發(fā)器可以保證數(shù)據(jù)的一致性和完整性啸澡。

?

每張表最多可建立12 種類型的觸發(fā)器袖订,它們是:

BEFORE INSERT

BEFORE INSERT FOR EACH ROW

AFTER INSERT

AFTER INSERT FOR EACH ROW


BEFORE UPDATE

BEFORE UPDATE FOR EACH ROW

AFTER UPDATE

AFTER UPDATE FOR EACH ROW


BEFORE DELETE

BEFORE DELETE FOR EACH ROW

AFTER DELETE

AFTER DELETE FOR EACH ROW


8.2.1 觸發(fā)器觸發(fā)次序

1.???????執(zhí)行 BEFORE語(yǔ)句級(jí)觸發(fā)器;

2.???????對(duì)與受語(yǔ)句影響的每一行:

l???????? 執(zhí)行 BEFORE行級(jí)觸發(fā)器

l???????? 執(zhí)行 DML語(yǔ)句

l???????? 執(zhí)行 AFTER行級(jí)觸發(fā)器?

3.???????執(zhí)行 AFTER語(yǔ)句級(jí)觸發(fā)器


8.2.2 創(chuàng)建DML觸發(fā)器

??? 觸發(fā)器名與過(guò)程名和包的名字不一樣介杆,它是單獨(dú)的名字空間膀值,因而觸發(fā)器名可以和表或過(guò)程有相同的名字,但在一個(gè)模式中觸發(fā)器名不能相同。

?

DML觸發(fā)器的限制

l???????? CREATE TRIGGER語(yǔ)句文本的字符長(zhǎng)度不能超過(guò)32KB奴曙;

l???????? 觸發(fā)器體內(nèi)的SELECT 語(yǔ)句只能為SELECT … INTO …結(jié)構(gòu),或者為定義游標(biāo)所使用的SELECT 語(yǔ)句逢防。

l???????? 觸發(fā)器中不能使用數(shù)據(jù)庫(kù)事務(wù)控制語(yǔ)句 COMMIT; ROLLBACK, SVAEPOINT 語(yǔ)句犀农;

l???????? 由觸發(fā)器所調(diào)用的過(guò)程或函數(shù)也不能使用數(shù)據(jù)庫(kù)事務(wù)控制語(yǔ)句;

l???????? 觸發(fā)器中不能使用LONG, LONG RAW 類型硫眯;

l???????? 觸發(fā)器內(nèi)可以參照LOB 類型列的列值蕴侧,但不能通過(guò) :NEW 修改LOB列中的數(shù)據(jù);

?

DML觸發(fā)器基本要點(diǎn)

l觸發(fā)時(shí)機(jī):指定觸發(fā)器的觸發(fā)時(shí)間两入。如果指定為BEFORE净宵,則表示在執(zhí)行DML操作之前觸發(fā),以便防止某些錯(cuò)誤操作發(fā)生或?qū)崿F(xiàn)某些業(yè)務(wù)規(guī)則裹纳;如果指定為AFTER择葡,則表示在執(zhí)行DML操作之后觸發(fā),以便記錄該操作或做某些事后處理剃氧。

l觸發(fā)事件:引起觸發(fā)器被觸發(fā)的事件敏储,即DML操作(INSERT朋鞍、UPDATE已添、DELETE)。既可以是單個(gè)觸發(fā)事件滥酥,也可以是多個(gè)觸發(fā)事件的組合(只能使用OR邏輯組合更舞,不能使用AND邏輯組合)。

l條件謂詞:當(dāng)在觸發(fā)器中包含多個(gè)觸發(fā)事件(INSERT恨狈、UPDATE疏哗、DELETE)的組合時(shí),為了分別針對(duì)不同的事件進(jìn)行不同的處理禾怠,需要使用ORACLE提供的如下條件謂詞返奉。

1)。INSERTING:當(dāng)觸發(fā)事件是INSERT時(shí)吗氏,取值為T(mén)RUE芽偏,否則為FALSE。

2)弦讽。UPDATING [(column_1,column_2,…,column_x)]:當(dāng)觸發(fā)事件是UPDATE????? 時(shí)污尉,如果修改了column_x列膀哲,則取值為T(mén)RUE,否則為FALSE被碗。其中column_x是可選的某宪。

3)。DELETING:當(dāng)觸發(fā)事件是DELETE時(shí)锐朴,則取值為T(mén)RUE兴喂,否則為FALSE。

解發(fā)對(duì)象:指定觸發(fā)器是創(chuàng)建在哪個(gè)表焚志、視圖上衣迷。

l觸發(fā)類型:是語(yǔ)句級(jí)還是行級(jí)觸發(fā)器。

l觸發(fā)條件:由WHEN子句指定一個(gè)邏輯表達(dá)式酱酬,只允許在行級(jí)觸發(fā)器上指定觸發(fā)條件壶谒,指定UPDATING后面的列的列表。

?

問(wèn)題:當(dāng)觸發(fā)器被觸發(fā)時(shí)膳沽,要使用被插入汗菜、更新或刪除的記錄中的列值,有時(shí)要使用操作前贵少、??????? 后列的值.

實(shí)現(xiàn):??:NEW?修飾符訪問(wèn)操作完成后列的值

???????:OLD?修飾符訪問(wèn)操作完成前列的值


特性INSERTUPDATEDELETE

OLDNULL實(shí)際值實(shí)際值

NEW實(shí)際值實(shí)際值NULL


例1:建立一個(gè)觸發(fā)器, 當(dāng)職工表 emp 表被刪除一條記錄時(shí)呵俏,把被刪除記錄寫(xiě)到職工表刪除日志表中去。



CREATE?TABLE?emp_his?AS?SELECT?*?FROM?EMP?WHERE1=2;

CREATE?OR?REPLACE?TRIGGER?tr_del_emp

BEFORE?DELETE?--指定觸發(fā)時(shí)機(jī)為刪除操作前觸發(fā)

ON?scott.emp

FOR?EACH?ROW???--說(shuō)明創(chuàng)建的是行級(jí)觸發(fā)器

BEGIN

--將修改前數(shù)據(jù)插入到日志記錄表?del_emp?,以供監(jiān)督使用滔灶。

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;

DELETE?emp?WHERE?empno=7788;

DROP?TABLE?emp_his;

DROP?TRIGGER?del_emp;



例2:限制對(duì)Departments表修改(包括INSERT,DELETE,UPDATE)的時(shí)間范圍普碎,即不允許在非工作時(shí)間修改departments表。



CREATE?OR?REPLACE?TRIGGER?tr_dept_time

BEFORE?INSERT?OR?DELETE?OR?UPDATE

ON?departments

BEGIN

IF?(TO_CHAR(sysdate,'DAY')?IN?('星期六',?'星期日'))?OR?(TO_CHAR(sysdate,?'HH24:MI')?NOT?BETWEEN?'08:30'?AND?'18:00')?THEN

RAISE_APPLICATION_ERROR(-20001,?'不是上班時(shí)間录平,不能修改departments表');

END?IF;

END;



例3:限定只對(duì)部門(mén)號(hào)為80的記錄進(jìn)行行觸發(fā)器操作麻车。



CREATE?OR?REPLACE?TRIGGER?tr_emp_sal_comm

BEFORE?UPDATE?OF?salary,?commission_pct

OR?DELETE

ON?HR.employees

FOR?EACH?ROW

WHEN?(old.department_id?=80)

BEGIN

CASE

WHEN?UPDATING?('salary')?THEN

IF?:NEW.salary?<?:old.salary?THEN

RAISE_APPLICATION_ERROR(-20001,?'部門(mén)80的人員的工資不能降');

END?IF;

WHEN?UPDATING?('commission_pct')?THEN

IF?:NEW.commission_pct?<?:old.commission_pct?THEN

RAISE_APPLICATION_ERROR(-20002,?'部門(mén)80的人員的獎(jiǎng)金不能降');

END?IF;

WHEN?DELETING?THEN

RAISE_APPLICATION_ERROR(-20003,?'不能刪除部門(mén)80的人員記錄');

END?CASE;

END;

/*

實(shí)例:

UPDATE?employees?SET?salary?=?8000?WHERE?employee_id?=?177;

DELETE?FROM?employees?WHERE?employee_id?in?(177,170);

*/



例4:利用行觸發(fā)器實(shí)現(xiàn)級(jí)聯(lián)更新。在修改了主表regions中的region_id之后(AFTER)斗这,級(jí)聯(lián)的动猬、自動(dòng)的更新子表countries表中原來(lái)在該地區(qū)的國(guó)家的region_id。




CREATE?OR?REPLACE?TRIGGER?tr_reg_cou

AFTER?update?OF?region_id

ON?regions

FOR?EACH?ROW

BEGIN

DBMS_OUTPUT.PUT_LINE('舊的region_id值是'||:old.region_id

||'表箭、新的region_id值是'||:new.region_id);

UPDATE?countries?SET?region_id?=?:new.region_id

WHERE?region_id?=?:old.region_id;

END;


例5:在觸發(fā)器中調(diào)用過(guò)程赁咙。



CREATE?OR?REPLACE?PROCEDURE?add_job_history

(?p_emp_id??????????job_history.employee_id%type

,?p_start_date??????job_history.start_date%type

,?p_end_date????????job_history.end_date%type

,?p_job_id??????????job_history.job_id%type

,?p_department_id???job_history.department_id%type

)

IS

BEGIN

INSERT?INTO?job_history?(employee_id,?start_date,?end_date,

job_id,?department_id)

VALUES(p_emp_id,?p_start_date,?p_end_date,?p_job_id,?p_department_id);

END?add_job_history;

--創(chuàng)建觸發(fā)器調(diào)用存儲(chǔ)過(guò)程...

CREATE?OR?REPLACE?TRIGGER?update_job_history

AFTER?UPDATE?OF?job_id,?department_id?ON?employees

FOR?EACH?ROW

BEGIN

add_job_history(:old.employee_id,?:old.hire_date,?sysdate,

:old.job_id,?:old.department_id);

END;



8.2.3 創(chuàng)建替代(INSTEAD OF)觸發(fā)器


創(chuàng)建觸發(fā)器的一般語(yǔ)法是:



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?]?--因?yàn)镮NSTEAD?OF觸發(fā)器只能在行級(jí)上觸發(fā),所以沒(méi)有必要指定

[WHEN?condition]

PL/SQL_block?|?CALL?procedure_name;



其中:

INSTEAD OF 選項(xiàng)使ORACLE激活觸發(fā)器,而不執(zhí)行觸發(fā)事件免钻。只能對(duì)視圖和對(duì)象視圖建立INSTEAD OF觸發(fā)器彼水,而不能對(duì)表、模式和數(shù)據(jù)庫(kù)建立INSTEAD OF 觸發(fā)器极舔。

???????????FOR EACH ROW選項(xiàng)說(shuō)明觸發(fā)器為行觸發(fā)器凤覆。行觸發(fā)器和語(yǔ)句觸發(fā)器的區(qū)別表現(xiàn)在:行觸發(fā)器要求當(dāng)一個(gè)DML語(yǔ)句操走影響數(shù)據(jù)庫(kù)中的多行數(shù)據(jù)時(shí),對(duì)于其中的每個(gè)數(shù)據(jù)行拆魏,只要它們符合觸發(fā)約束條件盯桦,均激活一次觸發(fā)器慈俯;而語(yǔ)句觸發(fā)器將整個(gè)語(yǔ)句操作作為觸發(fā)事件,當(dāng)它符合約束條件時(shí)拥峦,激活一次觸發(fā)器贴膘。當(dāng)省略FOR EACH ROW 選項(xiàng)時(shí),BEFORE 和AFTER 觸發(fā)器為語(yǔ)句觸發(fā)器事镣,而INSTEAD OF 觸發(fā)器則為行觸發(fā)器步鉴。

???????????REFERENCING 子句說(shuō)明相關(guān)名稱,在行觸發(fā)器的PL/SQL塊和WHEN 子句中可以使用相關(guān)名稱參照當(dāng)前的新璃哟、舊列值,默認(rèn)的相關(guān)名稱分別為OLD和NEW喊递。觸發(fā)器的PL/SQL塊中應(yīng)用相關(guān)名稱時(shí)随闪,必須在它們之前加冒號(hào)(:),但在WHEN子句中則不能加冒號(hào)骚勘。

WHEN 子句說(shuō)明觸發(fā)約束條件铐伴。Condition 為一個(gè)邏輯表達(dá)時(shí),其中必須包含相關(guān)名稱俏讹,而不能包含查詢語(yǔ)句当宴,也不能調(diào)用PL/SQL 函數(shù)。WHEN 子句指定的觸發(fā)約束條件只能用在BEFORE 和AFTER 行觸發(fā)器中泽疆,不能用在INSTEAD OF 行觸發(fā)器和其它類型的觸發(fā)器中户矢。


??? INSTEAD_OF 用于對(duì)視圖的DML觸發(fā),由于視圖有可能是由多個(gè)表進(jìn)行聯(lián)結(jié)(join)而成殉疼,因而并非是所有的聯(lián)結(jié)都是可更新的梯浪。但可以按照所需的方式執(zhí)行更新,例如下面情況:

例1:


CREATE?OR?REPLACE?VIEW?emp_view?AS

SELECT?deptno,?count(*)?total_employeer,?sum(sal)?total_salary

FROM?emp?GROUP?BY?deptno;


在此視圖中直接刪除是非法:

SQL>DELETE?FROM?emp_view?WHERE?deptno=10;

DELETE?FROM?emp_view?WHERE?deptno=10


ERROR 位于第 1 行:

ORA-01732: 此視圖的數(shù)據(jù)操縱操作非法


但是我們可以創(chuàng)建INSTEAD_OF觸發(fā)器來(lái)為 DELETE 操作執(zhí)行所需的處理瓢娜,即刪除EMP表中所有基準(zhǔn)行:

?


CREATE?OR?REPLACE?TRIGGER?emp_view_delete

INSTEAD?OF?DELETE?ON?emp_view?FOR?EACH?ROW

BEGIN

DELETE?FROM?emp?WHERE?deptno=?:old.deptno;

END?emp_view_delete;

DELETE?FROM?emp_view?WHERE?deptno=10;

DROP?TRIGGER?emp_view_delete;

DROP?VIEW?emp_view;



例2:創(chuàng)建復(fù)雜視圖挂洛,針對(duì)INSERT操作創(chuàng)建INSTEAD OF觸發(fā)器,向復(fù)雜視圖插入數(shù)據(jù)眠砾。

l???????? 創(chuàng)建視圖:



CREATE?OR?REPLACE?FORCE?VIEW?"HR"."V_REG_COU"?("R_ID",?"R_NAME",?"C_ID",?"C_NAME")

AS

SELECT?r.region_id,

r.region_name,

c.country_id,

c.country_name

FROM?regions?r,

countries?c

WHERE?r.region_id?=?c.region_id;



l????????創(chuàng)建觸發(fā)器:

?


CREATE?OR?REPLACE?TRIGGER?"HR"."TR_I_O_REG_COU"?INSTEAD?OF

INSERT?ON?v_reg_cou?FOR?EACH?ROW?DECLARE?v_count?NUMBER;

BEGIN

SELECT?COUNT(*)?INTO?v_count?FROM?regions?WHERE?region_id?=?:new.r_id;

IF?v_count?=?0?THEN

INSERT?INTO?regions

(region_id,?region_name

)?VALUES

(:new.r_id,?:new.r_name

);

END?IF;

SELECT?COUNT(*)?INTO?v_count?FROM?countries?WHERE?country_id?=?:new.c_id;

IF?v_count?=?0?THEN

INSERT

INTO?countries

(

country_id,

country_name,

region_id

)

VALUES

(

:new.c_id,

:new.c_name,

:new.r_id

);

END?IF;

END;


?

創(chuàng)建INSTEAD OF觸發(fā)器需要注意以下幾點(diǎn):

l???????? 只能被創(chuàng)建在視圖上虏劲,并且該視圖沒(méi)有指定WITH CHECK OPTION選項(xiàng)。

l???????? 不能指定BEFORE 或 AFTER選項(xiàng)褒颈。

l???????? FOR EACH ROW子可是可選的柒巫,即INSTEAD OF觸發(fā)器只能在行級(jí)上觸發(fā)、或只能是行級(jí)觸發(fā)器哈肖,沒(méi)有必要指定吻育。

l???????? 沒(méi)有必要在針對(duì)一個(gè)表的視圖上創(chuàng)建INSTEAD OF觸發(fā)器,只要?jiǎng)?chuàng)建DML觸發(fā)器就可以了淤井。

?

8.2.3 創(chuàng)建系統(tǒng)事件觸發(fā)器

??? ORACLE10G提供的系統(tǒng)事件觸發(fā)器可以在DDL或數(shù)據(jù)庫(kù)系統(tǒng)上被觸發(fā)布疼。DDL指的是數(shù)據(jù)定義語(yǔ)言摊趾,如CREATE 、ALTER及DROP 等游两。而數(shù)據(jù)庫(kù)系統(tǒng)事件包括數(shù)據(jù)庫(kù)服務(wù)器的啟動(dòng)或關(guān)閉砾层,用戶的登錄與退出、數(shù)據(jù)庫(kù)服務(wù)錯(cuò)誤等贱案。創(chuàng)建系統(tǒng)觸發(fā)器的語(yǔ)法如下:?

創(chuàng)建觸發(fā)器的一般語(yǔ)法是:



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;



其中: ddl_event_list:一個(gè)或多個(gè)DDL 事件肛炮,事件間用 OR 分開(kāi);

????????database_event_list:一個(gè)或多個(gè)數(shù)據(jù)庫(kù)事件宝踪,事件間用 OR 分開(kāi)侨糟;


???????????系統(tǒng)事件觸發(fā)器既可以建立在一個(gè)模式上,又可以建立在整個(gè)數(shù)據(jù)庫(kù)上瘩燥。當(dāng)建立在模式(SCHEMA)之上時(shí)秕重,只有模式所指定用戶的DDL操作和它們所導(dǎo)致的錯(cuò)誤才激活觸發(fā)器, 默認(rèn)時(shí)為當(dāng)前用戶模式。當(dāng)建立在數(shù)據(jù)庫(kù)(DATABASE)之上時(shí)厉膀,該數(shù)據(jù)庫(kù)所有用戶的DDL操作和他們所導(dǎo)致的錯(cuò)誤溶耘,以及數(shù)據(jù)庫(kù)的啟動(dòng)和關(guān)閉均可激活觸發(fā)器。要在數(shù)據(jù)庫(kù)之上建立觸發(fā)器時(shí)服鹅,要求用戶具有ADMINISTER DATABASE TRIGGER權(quán)限凳兵。


下面給出系統(tǒng)觸發(fā)器的種類和事件出現(xiàn)的時(shí)機(jī)(前或后):

事件允許的時(shí)機(jī)說(shuō)明

STARTUPAFTER啟動(dòng)數(shù)據(jù)庫(kù)實(shí)例之后觸發(fā)

SHUTDOWNBEFORE關(guān)閉數(shù)據(jù)庫(kù)實(shí)例之前觸發(fā)(非正常關(guān)閉不觸發(fā))

SERVERERRORAFTER數(shù)據(jù)庫(kù)服務(wù)器發(fā)生錯(cuò)誤之后觸發(fā)

LOGONAFTER成功登錄連接到數(shù)據(jù)庫(kù)后觸發(fā)

LOGOFFBEFORE開(kāi)始斷開(kāi)數(shù)據(jù)庫(kù)連接之前觸發(fā)

CREATEBEFORE,AFTER在執(zhí)行CREATE語(yǔ)句創(chuàng)建數(shù)據(jù)庫(kù)對(duì)象之前企软、之后觸發(fā)

DROPBEFORE庐扫,AFTER在執(zhí)行DROP語(yǔ)句刪除數(shù)據(jù)庫(kù)對(duì)象之前、之后觸發(fā)

ALTERBEFORE澜倦,AFTER在執(zhí)行ALTER語(yǔ)句更新數(shù)據(jù)庫(kù)對(duì)象之前聚蝶、之后觸發(fā)

DDLBEFORE,AFTER在執(zhí)行大多數(shù)DDL語(yǔ)句之前藻治、之后觸發(fā)

GRANTBEFORE碘勉,AFTER執(zhí)行GRANT語(yǔ)句授予權(quán)限之前、之后觸發(fā)

REVOKEBEFORE桩卵,AFTER執(zhí)行REVOKE語(yǔ)句收權(quán)限之前验靡、之后觸犯發(fā)

RENAMEBEFORE,AFTER執(zhí)行RENAME語(yǔ)句更改數(shù)據(jù)庫(kù)對(duì)象名稱之前雏节、之后觸犯發(fā)

AUDIT/?NOAUDITBEFORE胜嗓,AFTER執(zhí)行AUDITNOAUDIT進(jìn)行審計(jì)或停止審計(jì)之前、之后觸發(fā)



8.2.4 系統(tǒng)觸發(fā)器事件屬性


事件屬性\事件Startup/ShutdownServererrorLogon/LogoffDDLDML

事件名稱ü*ü*ü*ü*

數(shù)據(jù)庫(kù)名稱ü*????

數(shù)據(jù)庫(kù)實(shí)例號(hào)ü*????

錯(cuò)誤號(hào)?ü*???

用戶名??ü*?

模式對(duì)象類型???ü*

模式對(duì)象名稱???ü*

????ü*


除DML語(yǔ)句的列屬性外钩乍,其余事件屬性值可通過(guò)調(diào)用ORACLE定義的事件屬性函數(shù)來(lái)讀取辞州。

函數(shù)名稱數(shù)據(jù)類型說(shuō)??? 明

Ora_syseventVARCHAR2(20)激活觸發(fā)器的事件名稱

Instance_numNUMBER數(shù)據(jù)庫(kù)實(shí)例名

Ora_database_nameVARCHAR2(50)數(shù)據(jù)庫(kù)名稱

Server_error(posi)NUMBER錯(cuò)誤信息棧中posi指定位置中的錯(cuò)誤號(hào)



Is_servererror(err_number)



BOOLEAN

檢查err_number指定的錯(cuò)誤號(hào)是否在錯(cuò)誤信息棧中,如果在則返回TRUE寥粹,否則返回FALSE变过。在觸發(fā)器內(nèi)調(diào)用此函數(shù)可以判斷是否發(fā)生指定的錯(cuò)誤埃元。

Login_userVARCHAR2(30)登陸或注銷的用戶名稱

Dictionary_obj_typeVARCHAR2(20)DDL語(yǔ)句所操作的數(shù)據(jù)庫(kù)對(duì)象類型

Dictionary_obj_nameVARCHAR2(30)DDL語(yǔ)句所操作的數(shù)據(jù)庫(kù)對(duì)象名稱

Dictionary_obj_ownerVARCHAR2(30)DDL語(yǔ)句所操作的數(shù)據(jù)庫(kù)對(duì)象所有者名稱

Des_encrypted_passwordVARCHAR2(2)正在創(chuàng)建或修改的經(jīng)過(guò)DES算法加密的用戶口令

?

例1:創(chuàng)建觸發(fā)器,存放有關(guān)事件信息媚狰。


DESC?ora_sysevent

DESC?ora_login_user

--創(chuàng)建用于記錄事件用的表

CREATE?TABLE?ddl_event

(crt_date?timestamp?PRIMARY?KEY,

event_name?VARCHAR2(20),

user_name?VARCHAR2(10),

obj_type?VARCHAR2(20),

obj_name?VARCHAR2(20));

--創(chuàng)建觸犯發(fā)器

CREATE?OR?REPLACE?TRIGGER?tr_ddl

AFTER?DDL?ON?SCHEMA

BEGIN

INSERT?INTO?ddl_event?VALUES

(systimestamp,ora_sysevent,?ora_login_user,

ora_dict_obj_type,?ora_dict_obj_name);

END?tr_ddl;



例2:創(chuàng)建登錄岛杀、退出觸發(fā)器。



CREATE?TABLE?log_event

(user_name?VARCHAR2(10),

address?VARCHAR2(20),

logon_date?timestamp,

logoff_date?timestamp);

--創(chuàng)建登錄觸發(fā)器

CREATE?OR?REPLACE?TRIGGER?tr_logon

AFTER?LOGON?ON?DATABASE

BEGIN

INSERT?INTO?log_event?(user_name,?address,?logon_date)

VALUES?(ora_login_user,?ora_client_ip_address,?systimestamp);

END?tr_logon;

--創(chuàng)建退出觸發(fā)器

CREATE?OR?REPLACE?TRIGGER?tr_logoff

BEFORE?LOGOFF?ON?DATABASE

BEGIN

INSERT?INTO?log_event?(user_name,?address,?logoff_date)

VALUES?(ora_login_user,?ora_client_ip_address,?systimestamp);

END?tr_logoff;



8.2.5 使用觸發(fā)器謂詞

??? ORACLE 提供三個(gè)參數(shù)INSERTING, UPDATING,DELETING 用于判斷觸發(fā)了哪些操作崭孤。

謂詞行為

INSERTING如果觸發(fā)語(yǔ)句是 INSERT 語(yǔ)句类嗤,則為T(mén)RUE,否則為FALSE

UPDATING如果觸發(fā)語(yǔ)句是 UPDATE語(yǔ)句,則為T(mén)RUE,否則為FALSE

DELETING如果觸發(fā)語(yǔ)句是 DELETE 語(yǔ)句辨宠,則為T(mén)RUE,否則為FALSE

?

8.2.6 重新編譯觸發(fā)器

如果在觸發(fā)器內(nèi)調(diào)用其它函數(shù)或過(guò)程遗锣,當(dāng)這些函數(shù)或過(guò)程被刪除或修改后,觸發(fā)器的狀態(tài)將被標(biāo)識(shí)為無(wú)效彭羹。當(dāng)DML語(yǔ)句激活一個(gè)無(wú)效觸發(fā)器時(shí)黄伊,ORACLE將重新編譯觸發(fā)器代碼,如果編譯時(shí)發(fā)現(xiàn)錯(cuò)誤派殷,這將導(dǎo)致DML語(yǔ)句執(zhí)行失敗。

在PL/SQL程序中可以調(diào)用ALTER TRIGGER語(yǔ)句重新編譯已經(jīng)創(chuàng)建的觸發(fā)器墓阀,格式為:???????????

ALTER?TRIGGER?[schema.]?trigger_name?COMPILE?[?DEBUG]

?????? 其中:DEBUG 選項(xiàng)要器編譯器生成PL/SQL 程序條使其所使用的調(diào)試代碼毡惜。

8.3?刪除和使能觸發(fā)器

l刪除觸發(fā)器:

DROP?TRIGGER?trigger_name;

當(dāng)刪除其他用戶模式中的觸發(fā)器名稱,需要具有DROP ANY TRIGGER系統(tǒng)權(quán)限斯撮,當(dāng)刪除建立在數(shù)據(jù)庫(kù)上的觸發(fā)器時(shí)经伙,用戶需要具有ADMINISTER DATABASE TRIGGER系統(tǒng)權(quán)限。

此外勿锅,當(dāng)刪除表或視圖時(shí)帕膜,建立在這些對(duì)象上的觸發(fā)器也隨之刪除。?

l禁用或啟用觸發(fā)器

數(shù)據(jù)庫(kù)TRIGGER 的狀態(tài):

有效狀態(tài)(ENABLE):當(dāng)觸發(fā)事件發(fā)生時(shí)溢十,處于有效狀態(tài)的數(shù)據(jù)庫(kù)觸發(fā)器TRIGGER 將被觸發(fā)垮刹。

無(wú)效狀態(tài)(DISABLE):當(dāng)觸發(fā)事件發(fā)生時(shí),處于無(wú)效狀態(tài)的數(shù)據(jù)庫(kù)觸發(fā)器TRIGGER 將不會(huì)被觸發(fā)张弛,此時(shí)就跟沒(méi)有這個(gè)數(shù)據(jù)庫(kù)觸發(fā)器(TRIGGER) 一樣荒典。

數(shù)據(jù)庫(kù)TRIGGER的這兩種狀態(tài)可以互相轉(zhuǎn)換。格式為:

ALTER?TIGGER?trigger_name?[DISABLE?|?ENABLE?];

--例:ALTER?TRIGGER?emp_view_delete?DISABLE;


???????????ALTER TRIGGER語(yǔ)句一次只能改變一個(gè)觸發(fā)器的狀態(tài)吞鸭,而ALTER TABLE語(yǔ)句則一次能夠改變與指定表相關(guān)的所有觸發(fā)器的使用狀態(tài)寺董。格式為:?????????????

ALTER?TABLE?[schema.]table_name?{ENABLE|DISABLE}?ALL?TRIGGERS;

--例:使表EMP?上的所有TRIGGER?失效:

ALTER?TABLE?emp?DISABLE?ALL?TRIGGERS;


8.4?觸發(fā)器和數(shù)據(jù)字典

相關(guān)數(shù)據(jù)字典:USER_TRIGGERS、ALL_TRIGGERS刻剥、DBA_TRIGGERS?

SELECT?TRIGGER_NAME,?TRIGGER_TYPE,?TRIGGERING_EVENT,

TABLE_OWNER,?BASE_OBJECT_TYPE,?REFERENCING_NAMES,

STATUS,?ACTION_TYPE

FROM?user_triggers;


8.5?? 數(shù)據(jù)庫(kù)觸發(fā)器的應(yīng)用舉例

例1:創(chuàng)建一個(gè)DML語(yǔ)句級(jí)觸發(fā)器遮咖,當(dāng)對(duì)emp表執(zhí)行INSERT, UPDATE, DELETE 操作時(shí),它自動(dòng)更新dept_summary 表中的數(shù)據(jù)造虏。由于在PL/SQL塊中不能直接調(diào)用DDL語(yǔ)句御吞,所以麦箍,利用ORACLE內(nèi)置包DBMS_UTILITY中的EXEC_DDL_STATEMENT過(guò)程,由它執(zhí)行DDL語(yǔ)句創(chuàng)建觸發(fā)器魄藕。



CREATE?TABLE?dept_summary(

Deptno?NUMBER(2),

Sal_sum?NUMBER(9,2),

Emp_count?NUMBER);

INSERT?INTO?dept_summary(deptno,?sal_sum,?emp_count)

SELECT?deptno,?SUM(sal),?COUNT(*)

FROM?emp

GROUP?BY?deptno;

--創(chuàng)建一個(gè)PL/SQL過(guò)程disp_dept_summary

--在觸發(fā)器中調(diào)用該過(guò)程顯示dept_summary標(biāo)中的數(shù)據(jù)内列。

CREATE?OR?REPLACE?PROCEDURE?disp_dept_summary

IS

Rec?dept_summary%ROWTYPE;

CURSOR?c1?IS?SELECT?*?FROM?dept_summary;

BEGIN

OPEN?c1;

FETCH?c1?INTO?REC;

DBMS_OUTPUT.PUT_LINE('deptno????sal_sum????emp_count');

DBMS_OUTPUT.PUT_LINE('-------------------------------------');

WHILE?c1%FOUND?LOOP

DBMS_OUTPUT.PUT_LINE(RPAD(rec.deptno,6)||

To_char(rec.sal_sum,?'$999,999.99')||

LPAD(rec.emp_count,13));

FETCH?c1?INTO?rec;

END?LOOP;

CLOSE?c1;

END;

BEGIN

DBMS_OUTPUT.PUT_LINE('插入前');

Disp_dept_summary();

DBMS_UTILITY.EXEC_DDL_STATEMENT('

CREATE?OR?REPLACE?TRIGGER?trig1

AFTER?INSERT?OR?DELETE?OR?UPDATE?OF?sal?ON?emp

BEGIN

DBMS_OUTPUT.PUT_LINE(''正在執(zhí)行trig1?觸發(fā)器…'');

DELETE?FROM?dept_summary;

INSERT?INTO?dept_summary(deptno,?sal_sum,?emp_count)

SELECT?deptno,?SUM(sal),?COUNT(*)

FROM?emp?GROUP?BY?deptno;

END;

');

INSERT?INTO?dept(deptno,?dname,?loc)

VALUES(90,?‘demo_dept’,?‘none_loc’);

INSERT?INTO?emp(ename,?deptno,?empno,?sal)

VALUES(USER,90,9999,3000);

DBMS_OUTPUT.PUT_LINE('插入后');

Disp_dept_summary();

UPDATE?emp?SET?sal=1000WHERE?empno=9999;

DBMS_OUTPUT.PUT_LINE('修改后');

Disp_dept_summary();

DELETE?FROM?emp?WHERE?empno=9999;

DELETE?FROM?dept?WHERE?deptno=90;

DBMS_OUTPUT.PUT_LINE('刪除后');

Disp_dept_summary();

DBMS_UTILITY.EXEC_DDL_STATEMENT(‘DROP?TRIGGER?trig1’);

EXCEPTION

WHEN?OTHERS?THEN

DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);

END;



例2:創(chuàng)建DML語(yǔ)句行級(jí)觸發(fā)器。當(dāng)對(duì)emp表執(zhí)行INSERT, UPDATE, DELETE 操作時(shí)背率,它自動(dòng)更新dept_summary 表中的數(shù)據(jù)话瞧。由于在PL/SQL塊中不能直接調(diào)用DDL語(yǔ)句,所以寝姿,利用ORACLE內(nèi)置包DBMS_UTILITY中的EXEC_DDL_STATEMENT過(guò)程交排,由它執(zhí)行DDL語(yǔ)句創(chuàng)建觸發(fā)器。



BEGIN

DBMS_OUTPUT.PUT_LINE('插入前');

Disp_dept_summary();

DBMS_UTILITY.EXEC_DDL_STATEMENT(

'CREATE?OR?REPLACE?TRIGGER?trig2_update

AFTER?UPDATE?OF?sal?ON?emp

REFERENCING?OLD?AS?old_emp?NEW?AS?new_emp

FOR?EACH?ROW

WHEN?(old_emp.sal?!=?new_emp.sal)

BEGIN

DBMS_OUTPUT.PUT_LINE(''正在執(zhí)行trig2_update?觸發(fā)器…'');

DBMS_OUTPUT.PUT_LINE(''sal?舊值:''||?:old_emp.sal);

DBMS_OUTPUT.PUT_LINE(''sal?新值:''||?:new_emp.sal);

UPDATE?dept_summary

SET?sal_sum=sal_sum?+?:new_emp.sal?-?:old_emp.sal

WHERE?deptno?=?:new_emp.deptno;

END;'

);

DBMS_UTILITY.EXEC_DDL_STATEMENT(

'CREATE?OR?REPLACE?TRIGGER?trig2_insert

AFTER?INSERT?ON?emp

REFERENCING?NEW?AS?new_emp

FOR?EACH?ROW

DECLARE

I?NUMBER;

BEGIN

DBMS_OUTPUT.PUT_LINE(''正在執(zhí)行trig2_insert?觸發(fā)器…'');

SELECT?COUNT(*)?INTO?I

FROM?dept_summary?WHERE?deptno?=?:new_emp.deptno;

IF?I?>?0?THEN

UPDATE?dept_summary

SET?sal_sum=sal_sum+:new_emp.sal,

Emp_count=emp_count+1

WHERE?deptno?=?:new_emp.deptno;

ELSE

INSERT?INTO?dept_summary

VALUES?(:new_emp.deptno,?:new_emp.sal,?1);

END?IF;

END;'

);

DBMS_UTILITY.EXEC_DDL_STATEMENT(

'CREATE?OR?REPLACE?TRIGGER?trig2_delete

AFTER?DELETE?ON?emp

REFERENCING?OLD?AS?old_emp

FOR?EACH?ROW

DECLARE

I?NUMBER;

BEGIN

DBMS_OUTPUT.PUT_LINE(''正在執(zhí)行trig2_delete?觸發(fā)器…'');

SELECT?emp_count?INTO?I

FROM?dept_summary?WHERE?deptno?=?:old_emp.deptno;

IF?I?>1?THEN

UPDATE?dept_summary

SET?sal_sum=sal_sum?-?:old_emp.sal,

Emp_count=emp_count?-?1

WHERE?deptno?=?:old_emp.deptno;

ELSE

DELETE?FROM?dept_summary?WHERE?deptno?=?:old_emp.deptno;

END?IF;

END;'

);

INSERT?INTO?dept(deptno,?dname,?loc)

VALUES(90,?'demo_dept',?'none_loc');

INSERT?INTO?emp(ename,?deptno,?empno,?sal)

VALUES(USER,90,9999,3000);

INSERT?INTO?emp(ename,?deptno,?empno,?sal)

VALUES(USER,90,9998,2000);

DBMS_OUTPUT.PUT_LINE('插入后');

Disp_dept_summary();

UPDATE?emp?SET?sal?=?sal*1.1WHERE?deptno=90;

DBMS_OUTPUT.PUT_LINE('修改后');

Disp_dept_summary();

DELETE?FROM?emp?WHERE?deptno=90;

DELETE?FROM?dept?WHERE?deptno=90;

DBMS_OUTPUT.PUT_LINE('刪除后');

Disp_dept_summary();

DBMS_UTILITY.EXEC_DDL_STATEMENT('DROP?TRIGGER?trig2_update');

DBMS_UTILITY.EXEC_DDL_STATEMENT('DROP?TRIGGER?trig2_insert');

DBMS_UTILITY.EXEC_DDL_STATEMENT('DROP?TRIGGER?trig2_delete');

EXCEPTION

WHEN?OTHERS?THEN

DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);

END;



例3:利用ORACLE提供的條件謂詞INSERTING饵筑、UPDATING和DELETING創(chuàng)建與例2具有相同功能的觸發(fā)器埃篓。



BEGIN

DBMS_OUTPUT.PUT_LINE('插入前');

Disp_dept_summary();

DBMS_UTILITY.EXEC_DDL_STATEMENT(

'CREATE?OR?REPLACE?TRIGGER?trig2

AFTER?INSERT?OR?DELETE?OR?UPDATE?OF?sal

ON?emp

REFERENCING?OLD?AS?old_emp?NEW?AS?new_emp

FOR?EACH?ROW

DECLARE

I?NUMBER;

BEGIN

IF?UPDATING?AND?:old_emp.sal?!=?:new_emp.sal?THEN

DBMS_OUTPUT.PUT_LINE(''正在執(zhí)行trig2?觸發(fā)器…'');

DBMS_OUTPUT.PUT_LINE(''sal?舊值:''||?:old_emp.sal);

DBMS_OUTPUT.PUT_LINE(''sal?新值:''||?:new_emp.sal);

UPDATE?dept_summary

SET?sal_sum=sal_sum?+?:new_emp.sal?-?:old_emp.sal

WHERE?deptno?=?:new_emp.deptno;

ELSIF?INSERTING?THEN

DBMS_OUTPUT.PUT_LINE(''正在執(zhí)行trig2觸發(fā)器…'');

SELECT?COUNT(*)?INTO?I

FROM?dept_summary

WHERE?deptno?=?:new_emp.deptno;

IF?I?>?0?THEN

UPDATE?dept_summary

SET?sal_sum=sal_sum+:new_emp.sal,

Emp_count=emp_count+1

WHERE?deptno?=?:new_emp.deptno;

ELSE

INSERT?INTO?dept_summary

VALUES?(:new_emp.deptno,?:new_emp.sal,?1);

END?IF;

ELSE

DBMS_OUTPUT.PUT_LINE(''正在執(zhí)行trig2觸發(fā)器…'');

SELECT?emp_count?INTO?I

FROM?dept_summary?WHERE?deptno?=?:old_emp.deptno;

IF?I?>?1?THEN

UPDATE?dept_summary

SET?sal_sum=sal_sum?-?:old_emp.sal,

Emp_count=emp_count?-?1

WHERE?deptno?=?:old_emp.deptno;

ELSE

DELETE?FROM?dept_summary

WHERE?deptno?=?:old_emp.deptno;

END?IF;

END?IF;

END;'

);

INSERT?INTO?dept(deptno,?dname,?loc)

VALUES(90,?'demo_dept',?'none_loc');

INSERT?INTO?emp(ename,?deptno,?empno,?sal)

VALUES(USER,90,9999,3000);

INSERT?INTO?emp(ename,?deptno,?empno,?sal)

VALUES(USER,90,9998,2000);

DBMS_OUTPUT.PUT_LINE('插入后');

Disp_dept_summary();

UPDATE?emp?SET?sal?=?sal*1.1WHERE?deptno=90;

DBMS_OUTPUT.PUT_LINE('修改后');

Disp_dept_summary();

DELETE?FROM?emp?WHERE?deptno=90;

DELETE?FROM?dept?WHERE?deptno=90;

DBMS_OUTPUT.PUT_LINE('刪除后');

Disp_dept_summary();

DBMS_UTILITY.EXEC_DDL_STATEMENT('DROP?TRIGGER?trig2');

EXCEPTION

WHEN?OTHERS?THEN

DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);

END;



例4:創(chuàng)建INSTEAD OF 觸發(fā)器。首先創(chuàng)建一個(gè)視圖myview,由于該視圖是復(fù)合查詢所產(chǎn)生的視圖根资,所以不能執(zhí)行DML語(yǔ)句架专。根據(jù)用戶對(duì)視圖所插入的數(shù)據(jù)判斷需要將數(shù)據(jù)插入到哪個(gè)視圖基表中,然后對(duì)該基表執(zhí)行插入操作玄帕。



DECLARE

No?NUMBER;

Name?VARCHAR2(20);

BEGIN

DBMS_UTILITY.EXEC_DDL_STATEMENT('

CREATE?OR?REPLACE?VIEW?myview?AS

SELECT?empno,?ename,?''E''?type?FROM?emp

UNION

SELECT?dept.deptno,?dname,?''D''?FROM?dept

');

--?創(chuàng)建INSTEAD?OF?觸發(fā)器trigger3;

DBMS_UTILITY.EXEC_DDL_STATEMENT('

CREATE?OR?REPLACE?TRIGGER?trig3

INSTEAD?OF?INSERT?ON?myview

REFERENCING?NEW?n

FOR?EACH?ROW

DECLARE

Rows?INTEGER;

BEGIN

DBMS_OUTPUT.PUT_LINE(''正在執(zhí)行trig3觸發(fā)器…'');

IF?:n.type?=?''D''?THEN

SELECT?COUNT(*)?INTO?rows

FROM?dept?WHERE?deptno?=?:n.empno;

IF?rows?=?0?THEN

DBMS_OUTPUT.PUT_LINE(''向dept表中插入數(shù)據(jù)…'');

INSERT?INTO?dept(deptno,?dname,?loc)

VALUES?(:n.empno,?:n.ename,?''none’’);

ELSE

DBMS_OUTPUT.PUT_LINE(''編號(hào)為''||?:n.empno||

''的部門(mén)已存在部脚,插入操作失敗裤纹!'');

END?IF;

ELSE

SELECT?COUNT(*)?INTO?rows

FROM?emp?WHERE?empno?=?:n.empno;

IF?rows?=?0?THEN

DBMS_OUTPUT.PUT_LINE('’向emp表中插入數(shù)據(jù)…’’);

INSERT?INTO?emp(empno,?ename)

VALUES(:n.empno,?:n.ename);

ELSE

DBMS_OUTPUT.PUT_LINE(''編號(hào)為''||?:n.empno||

''的人員已存在委刘,插入操作失敗!'');

END?IF;

END?IF;

END;

');

INSERT?INTO?myview?VALUES?(70,?'demo',?'D');

INSERT?INTO?myview?VALUES?(9999,?USER,?'E');

SELECT?deptno,?dname?INTO?no,?name?FROM?dept?WHERE?deptno=70;

DBMS_OUTPUT.PUT_LINE('員工編號(hào):'||TO_CHAR(no)||'姓名:'||name);

SELECT?empno,?ename?INTO?no,?name?FROM?emp?WHERE?empno=9999;

DBMS_OUTPUT.PUT_LINE('部門(mén)編號(hào):'||TO_CHAR(no)||'姓名:'||name);

DELETE?FROM?emp?WHERE?empno=9999;

DELETE?FROM?dept?WHERE?deptno=70;

DBMS_UTILITY.EXEC_DDL_STATEMENT('DROP?TRIGGER?trig3');

END;



例5:利用ORACLE事件屬性函數(shù),創(chuàng)建一個(gè)系統(tǒng)事件觸發(fā)器鹰椒。首先創(chuàng)建一個(gè)事件日志表eventlog锡移,由它存儲(chǔ)用戶在當(dāng)前數(shù)據(jù)庫(kù)中所創(chuàng)建的數(shù)據(jù)庫(kù)對(duì)象,以及用戶的登陸和注銷漆际、數(shù)據(jù)庫(kù)的啟動(dòng)和關(guān)閉等事件淆珊,之后創(chuàng)建trig4_ddl、trig4_before和trig4_after觸發(fā)器灿椅,它們調(diào)用事件屬性函數(shù)將各個(gè)事件記錄到eventlog數(shù)據(jù)表中套蒂。

?


BEGIN

--?創(chuàng)建用于記錄事件日志的數(shù)據(jù)表

DBMS_UTILITY.EXEC_DDL_STATEMENT('

CREATE?TABLE?eventlog(

Eventname?VARCHAR2(20)?NOT?NULL,

Eventdate?date?default?sysdate,

Inst_num?NUMBER?NULL,

Db_name?VARCHAR2(50)?NULL,

Srv_error?NUMBER?NULL,

Username?VARCHAR2(30)?NULL,

Obj_type?VARCHAR2(20)?NULL,

Obj_name?VARCHAR2(30)?NULL,

Obj_owner?VARCHAR2(30)?NULL

)

');

--?創(chuàng)建DDL觸發(fā)器trig4_ddl

DBMS_UTILITY.EXEC_DDL_STATEMENT('

CREATE?OR?REPLACE?TRIGGER?trig4_ddl

AFTER?CREATE?OR?ALTER?OR?DROP

ON?DATABASE

DECLARE

Event?VARCHAR2(20);

Typ?VARCHAR2(20);

Name?VARCHAR2(30);

Owner?VARCHAR2(30);

BEGIN

--?讀取DDL事件屬性

Event?:=?SYSEVENT;

Typ?:=?DICTIONARY_OBJ_TYPE;

Name?:=?DICTIONARY_OBJ_NAME;

Owner?:=?DICTIONARY_OBJ_OWNER;

--將事件屬性插入到事件日志表中

INSERT?INTO?scott.eventlog(eventname,?obj_type,?obj_name,?obj_owner)

VALUES(event,?typ,?name,?owner);

END;

');

--?創(chuàng)建LOGON、STARTUP和SERVERERROR?事件觸發(fā)器

DBMS_UTILITY.EXEC_DDL_STATEMENT('

CREATE?OR?REPLACE?TRIGGER?trig4_after

AFTER?LOGON?OR?STARTUP?OR?SERVERERROR

ON?DATABASE

DECLARE

Event?VARCHAR2(20);

Instance?NUMBER;

Err_num?NUMBER;

Dbname?VARCHAR2(50);

User?VARCHAR2(30);

BEGIN

Event?:=?SYSEVENT;

IF?event?=?''LOGON''?THEN

User?:=?LOGIN_USER;

INSERT?INTO?eventlog(eventname,?username)

VALUES(event,?user);

ELSIF?event?=?''SERVERERROR''?THEN

Err_num?:=?SERVER_ERROR(1);

INSERT?INTO?eventlog(eventname,?srv_error)

VALUES(event,?err_num);

ELSE

Instance?:=?INSTANCE_NUM;

Dbname?:=?DATABASE_NAME;

INSERT?INTO?eventlog(eventname,?inst_num,?db_name)

VALUES(event,?instance,?dbname);

END?IF;

END;

');

--?創(chuàng)建LOGOFF和SHUTDOWN?事件觸發(fā)器

DBMS_UTILITY.EXEC_DDL_STATEMENT('

CREATE?OR?REPLACE?TRIGGER?trig4_before

BEFORE?LOGOFF?OR?SHUTDOWN

ON?DATABASE

DECLARE

Event?VARCHAR2(20);

Instance?NUMBER;

Dbname?VARCHAR2(50);

User?VARCHAR2(30);

BEGIN

Event?:=?SYSEVENT;

IF?event?=?''LOGOFF''?THEN

User?:=?LOGIN_USER;

INSERT?INTO?eventlog(eventname,?username)

VALUES(event,?user);

ELSE

Instance?:=?INSTANCE_NUM;

Dbname?:=?DATABASE_NAME;

INSERT?INTO?eventlog(eventname,?inst_num,?db_name)

VALUES(event,?instance,?dbname);

END?IF;

END;

');

END;

CREATE?TABLE?mydata(mydate?NUMBER);

CONNECT?SCOTT/TIGER

COL?eventname?FORMAT?A10

COL?eventdate?FORMAT?A12

COL?username?FORMAT?A10

COL?obj_type?FORMAT?A15

COL?obj_name?FORMAT?A15

COL?obj_owner?FORMAT?A10

SELECT?eventname,?eventdate,?obj_type,?obj_name,?obj_owner,?username,?Srv_error

FROM?eventlog;

DROP?TRIGGER?trig4_ddl;

DROP?TRIGGER?trig4_before;

DROP?TRIGGER?trig4_after;

DROP?TABLE?eventlog;

DROP?TABLE?mydata;



8.6?? 數(shù)據(jù)庫(kù)觸發(fā)器的應(yīng)用實(shí)例

用戶可以使用數(shù)據(jù)庫(kù)觸發(fā)器實(shí)現(xiàn)各種功能:

l???????? 復(fù)雜的審計(jì)功能茫蛹;

例:將EMP 表的變化情況記錄到AUDIT_TABLE和AUDIT_TABLE_VALUES中操刀。



CREATE?TABLE?audit_table(

Audit_id?????NUMBER,

User_name?VARCHAR2(20),

Now_time?DATE,

Terminal_name?VARCHAR2(10),

Table_name?VARCHAR2(10),

Action_name?VARCHAR2(10),

Emp_id?NUMBER(4));

CREATE?TABLE?audit_table_val(

Audit_id?NUMBER,

Column_name?VARCHAR2(10),

Old_val?NUMBER(7,2),

New_val?NUMBER(7,2));

CREATE?SEQUENCE?audit_seq

START?WITH1000

INCREMENT?BY1

NOMAXVALUE

NOCYCLE?NOCACHE;

CREATE?OR?REPLACE?TRIGGER?audit_emp

AFTER?INSERT?OR?UPDATE?OR?DELETE?ON?emp

FOR?EACH?ROW

DECLARE

Time_now?DATE;

Terminal?CHAR(10);

BEGIN

Time_now:=sysdate;

Terminal:=USERENV('TERMINAL');

IF?INSERTING?THEN

INSERT?INTO?audit_table

VALUES(audit_seq.NEXTVAL,?user,?time_now,

terminal,?'EMP',?'INSERT',?:new.empno);

ELSIF?DELETING?THEN

INSERT?INTO?audit_table

VALUES(audit_seq.NEXTVAL,?user,?time_now,

terminal,?'EMP',?'DELETE',?:old.empno);

ELSE

INSERT?INTO?audit_table

VALUES(audit_seq.NEXTVAL,?user,?time_now,

terminal,?'EMP',?'UPDATE',?:old.empno);

IF?UPDATING('SAL')?THEN

INSERT?INTO?audit_table_val

VALUES(audit_seq.CURRVAL,?'SAL',?:old.sal,?:new.sal);

ELSE?UPDATING('DEPTNO')

INSERT?INTO?audit_table_val

VALUES(audit_seq.CURRVAL,?'DEPTNO',?:old.deptno,?:new.deptno);

END?IF;

END?IF;

END;



l???????? 增強(qiáng)數(shù)據(jù)的完整性管理;

例:修改DEPT表的DEPTNO列時(shí)婴洼,同時(shí)把EMP表中相應(yīng)的DEPTNO也作相應(yīng)的修改骨坑;



CREATE?SEQUENCE?update_sequence

INCREMENT?BY1

START?WITH1000

MAXVALUE5000CYCLE;

ALTER?TABLE?emp

ADD?update_id?NUMBER;

CREATE?OR?REPLACE?PACKAGE?integritypackage?AS

Updateseq?NUMBER;

END?integritypackage;

CREATE?OR?REPLACE?PACKAGE?BODY?integritypackage?AS

END?integritypackage;

CREATE?OR?REPLACE?TRIGGER?dept_cascade1

BEFORE?UPDATE?OF?deptno?ON?dept

DECLARE

Dummy?NUMBER;

BEGIN

SELECT?update_sequence.NEXTVAL?INTO?dummy?FROM?dual;

Integritypackage.updateseq:=dummy;

END;

CREATE?OR?REPLACE?TRIGGER?dept_cascade2

AFTER?DELETE?OR?UPDATE?OF?deptno?ON?dept

FOR?EACH?ROW

BEGIN

IF?UPDATING?THEN

UPDATE?emp?SET?deptno=:new.deptno,

update_id=integritypackage.updateseq

WHERE?emp.deptno=:old.deptno?AND?update_id?IS?NULL;

END?IF;

IF?DELETING?THEN

DELETE?FROM?emp

WHERE?emp.deptno=:old.deptno;

END?IF;

END;

CREATE?OR?REPLACE?TRIGGER?dept_cascade3

AFTER?UPDATE?OF?deptno?ON?dept

BEGIN

UPDATE?emp?SET?update_id=NULL

WHERE?update_id=integritypackage.updateseq;

END;

SELECT?*?FROM?EMP?ORDER?BY?DEPTNO;

UPDATE?dept?SET?deptno=25WHERE?deptno=20;



l???????? 幫助實(shí)現(xiàn)安全控制;

例:保證對(duì)EMP表的修改僅在工作日的工作時(shí)間;



CREATE?TABLE?company_holidays(day?DATE);

INSERT?INTO?company_holidays

VALUES(sysdate);

INSERT?INTO?company_holidays

VALUES(TO_DATE('21-10月-01',?'DD-MON-YY'));

CREATE?OR?REPLACE?TRIGGER?emp_permit_change

BEFORE?INSERT?OR?DELETE?OR?UPDATE?ON?emp

DECLARE

Dummy?NUMBER;

Not_on_weekends?EXCEPTION;

Not_on_holidays?EXCEPTION;

Not_working_hours?EXCEPTION;

BEGIN

/*?check?for?weekends?*/

IF?TO_CHAR(SYSDATE,?'DAY')?IN?('星期六',?'星期日')?THEN

RAISE?not_on_weekends;

END?IF;

/*?check?for?company?holidays?*/

SELECT?COUNT(*)?INTO?dummy?FROM?company_holidays

WHERE?TRUNC(day)=TRUNC(SYSDATE);

IF?dummy?>0THEN

RAISE?not_on_holidays;

END?IF;

/*?check?for?work?hours(8:00?AM?to?18:00?PM?*/

IF?(TO_CHAR(SYSDATE,'HH24')<8OR?TO_CHAR(SYSDATE,?'HH24')>18)?THEN

RAISE?not_working_hours;

END?IF;

EXCEPTION

WHEN?not_on_weekends?THEN

RAISE_APPLICATION_ERROR(-20324,

'May?not?change?employee?table?during?the?weekends');

WHEN?not_on_holidays?THEN

RAISE_APPLICATION_ERROR(-20325,

'May?not?change?employee?table?during?a?holiday');

WHEN?not_working_hours?THEN

RAISE_APPLICATION_ERROR(-20326,

'May?not?change?employee?table?during?no_working?hours');

END;

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末欢唾,一起剝皮案震驚了整個(gè)濱河市且警,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌礁遣,老刑警劉巖斑芜,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異祟霍,居然都是意外死亡杏头,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)沸呐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)醇王,“玉大人,你說(shuō)我怎么就攤上這事崭添≡⒚洌” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵呼渣,是天一觀的道長(zhǎng)棘伴。 經(jīng)常有香客問(wèn)我,道長(zhǎng)屁置,這世上最難降的妖魔是什么排嫌? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任缰犁,我火速辦了婚禮,結(jié)果婚禮上怖糊,老公的妹妹穿的比我還像新娘帅容。我一直安慰自己,他們只是感情好伍伤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開(kāi)白布并徘。 她就那樣靜靜地躺著,像睡著了一般扰魂。 火紅的嫁衣襯著肌膚如雪麦乞。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,215評(píng)論 1 299
  • 那天劝评,我揣著相機(jī)與錄音姐直,去河邊找鬼。 笑死蒋畜,一個(gè)胖子當(dāng)著我的面吹牛声畏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼插龄,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼愿棋!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起均牢,我...
    開(kāi)封第一講書(shū)人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤糠雨,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后徘跪,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體甘邀,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年真椿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鹃答。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡突硝,死狀恐怖测摔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情解恰,我是刑警寧澤锋八,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站护盈,受9級(jí)特大地震影響挟纱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜腐宋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一紊服、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧胸竞,春花似錦欺嗤、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至校赤,卻和暖如春吆玖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背马篮。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工沾乘, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人积蔚。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓意鲸,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子怎顾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容