oracle 触发器的种类和触发事件
生活随笔
收集整理的這篇文章主要介紹了
oracle 触发器的种类和触发事件
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
觸發(fā)器的種類和觸發(fā)事件
?
觸發(fā)器必須由事件才能觸發(fā)。觸發(fā)器的觸發(fā)事件分可為3類,分別是DML事件、DDL事件和數(shù)據(jù)庫(kù)事件。?
每類事件包含若干個(gè)事件,如下所示。數(shù)據(jù)庫(kù)的事件是具體的,在創(chuàng)建觸發(fā)器時(shí)要指明觸發(fā)的事件。?
種? 類 關(guān) 鍵 字 含??? 義?
Sql代碼?? DML事件(3種)???INSERT??在表或視圖中插入數(shù)據(jù)時(shí)觸發(fā)?? ????UPDATE??修改表或視圖中的數(shù)據(jù)時(shí)觸發(fā)?? ????DELETE??在刪除表或視圖中的數(shù)據(jù)時(shí)觸發(fā)?? DDL事件(3種)???CREATE??在創(chuàng)建新對(duì)象時(shí)觸發(fā)?? ????ALTER???修改數(shù)據(jù)庫(kù)或數(shù)據(jù)庫(kù)對(duì)象時(shí)觸發(fā)?? ????DROP????刪除對(duì)象時(shí)觸發(fā)?? 數(shù)據(jù)庫(kù)事件(5種)???STARTUP?數(shù)據(jù)打開(kāi)時(shí)觸發(fā)?? ????SHUTDOWN????在使用NORMAL或IMMEDIATE選項(xiàng)關(guān)閉數(shù)據(jù)庫(kù)時(shí)觸發(fā)?? ????LOGON???當(dāng)用戶連接到數(shù)據(jù)庫(kù)并建立會(huì)話時(shí)觸發(fā)?? ????LOGOFF??當(dāng)一個(gè)會(huì)話從數(shù)據(jù)庫(kù)中斷開(kāi)時(shí)觸發(fā)?? ????SERVERERROR?發(fā)生服務(wù)器錯(cuò)誤時(shí)觸發(fā)??
觸發(fā)器的類型可劃分為4種:數(shù)據(jù)操縱語(yǔ)言(DML)觸發(fā)器、替代(INSTEAD OF)觸發(fā)器、數(shù)據(jù)定義語(yǔ)言(DDL)觸發(fā)器和數(shù)據(jù)庫(kù)事件觸發(fā)器。?
各類觸發(fā)器的作用如下所示。?
Sql代碼?? 種??類????簡(jiǎn)??稱????作????用?? 數(shù)據(jù)操縱語(yǔ)言觸發(fā)器???DML觸發(fā)器??創(chuàng)建在表上,由DML事件引發(fā)的觸發(fā)器?? 替代觸發(fā)器???INSTEAD?OF觸發(fā)器???創(chuàng)建在視圖上,用來(lái)替換對(duì)視圖進(jìn)行的插入、刪除和修改操作?? 數(shù)據(jù)定義語(yǔ)言觸發(fā)器???DDL觸發(fā)器??定義在模式上,觸發(fā)事件是數(shù)據(jù)庫(kù)對(duì)象的創(chuàng)建和修改?? 數(shù)據(jù)庫(kù)事件觸發(fā)器????—???定義在整個(gè)數(shù)據(jù)庫(kù)或模式上,觸發(fā)事件是數(shù)據(jù)庫(kù)事件??
DML觸發(fā)器的要點(diǎn) ?
DML觸發(fā)器是定義在表上的觸發(fā)器,由DML事件引發(fā)。編寫(xiě)DML觸發(fā)器的要素是:?
* 確定觸發(fā)的表,即在其上定義觸發(fā)器的表。?
* 確定觸發(fā)的事件,DML觸發(fā)器的觸發(fā)事件有INSERT、UPDATE和DELETE三種,說(shuō)明見(jiàn)下。?
* 確定觸發(fā)時(shí)間。觸發(fā)的時(shí)間有BEFORE和AFTER兩種,分別表示觸發(fā)動(dòng)作發(fā)生在DML語(yǔ)句執(zhí)行之前和語(yǔ)句執(zhí)行之后。?
* 確定觸發(fā)級(jí)別,有語(yǔ)句級(jí)觸發(fā)器和行級(jí)觸發(fā)器兩種。語(yǔ)句級(jí)觸發(fā)器表示SQL語(yǔ)句只觸發(fā)一次觸發(fā)器,行級(jí)觸發(fā)器表示SQL語(yǔ)句影響的每一行都要觸發(fā)一次。?
由于在同一個(gè)表上可以定義多個(gè)DML觸發(fā)器,因此觸發(fā)器本身和引發(fā)觸發(fā)器的SQL語(yǔ)句在執(zhí)行的順序上有先后的關(guān)系。它們的順序是:?
* 如果存在語(yǔ)句級(jí)BEFORE觸發(fā)器,則先執(zhí)行一次語(yǔ)句級(jí)BEFORE觸發(fā)器。?
* 在SQL語(yǔ)句的執(zhí)行過(guò)程中,如果存在行級(jí)BEFORE觸發(fā)器,則SQL語(yǔ)句在對(duì)每一行操作之前,都要先執(zhí)行一次行級(jí)BEFORE觸發(fā)器,然后才對(duì)行進(jìn)行操作。如果存在行級(jí)AFTER觸發(fā)器,則SQL語(yǔ)句在對(duì)每一行操作之后,都要再執(zhí)行一次行級(jí)AFTER觸發(fā)器。?
* 如果存在語(yǔ)句級(jí)AFTER觸發(fā)器,則在SQL語(yǔ)句執(zhí)行完畢后,要最后執(zhí)行一次語(yǔ)句級(jí)AFTER觸發(fā)器。?
DML觸發(fā)器還有一些具體的問(wèn)題,說(shuō)明如下:?
* 如果有多個(gè)觸發(fā)器被定義成為相同時(shí)間、相同事件觸發(fā),且最后定義的觸發(fā)器是有效的,則最后定義的觸發(fā)器被觸發(fā),其他觸發(fā)器不執(zhí)行。?
* 一個(gè)觸發(fā)器可由多個(gè)不同的DML操作觸發(fā)。在觸發(fā)器中,可用INSERTING、DELETING、UPDATING謂詞來(lái)區(qū)別不同的DML操作。這些謂詞可以在IF分支條件語(yǔ)句中作為判斷條件來(lái)使用。?
* 在行級(jí)觸發(fā)器中,用:new 和:old(稱為偽記錄)來(lái)訪問(wèn)數(shù)據(jù)變更前后的值。但要注意,INSERT語(yǔ)句插入一條新記錄,所以沒(méi)有:old記錄,而DELETE語(yǔ)句刪除掉一條已經(jīng)存在的記錄,所以沒(méi)有:new記錄。UPDATE語(yǔ)句既有:old記錄,也有:new記錄,分別代表修改前后的記錄。引用具體的某一列的值的方法是:?
??????? ld.字段名或:new.字段名?
* 觸發(fā)器體內(nèi)禁止使用COMMIT、ROLLBACK、SAVEPOINT語(yǔ)句,也禁止直接或間接地調(diào)用含有上述語(yǔ)句的存儲(chǔ)過(guò)程。?
定義一個(gè)觸發(fā)器時(shí)要考慮上述多種情況,并根據(jù)具體的需要來(lái)決定觸發(fā)器的種類。?
DML觸發(fā)器的創(chuàng)建 ?
創(chuàng)建DML觸發(fā)器需要CREATE TRIGGER系統(tǒng)權(quán)限。創(chuàng)建DML觸發(fā)器的語(yǔ)法如下:?
CREATE [OR REPLACE] TRIGGER 觸發(fā)器名?
{BEFORE|AFTER|INSTEAD OF} 觸發(fā)事件1 [OR 觸發(fā)事件2...]?
ON 表名?
WHEN 觸發(fā)條件?
[FOR EACH ROW]?
DECLARE?
聲明部分?
BEGIN?
主體部分?
END;?
其中:?
OR REPLACE:表示如果存在同名觸發(fā)器,則覆蓋原有同名觸發(fā)器。?
BEFORE、AFTER和INSTEAD OF:說(shuō)明觸發(fā)器的類型。?
WHEN 觸發(fā)條件:表示當(dāng)該條件滿足時(shí),觸發(fā)器才能執(zhí)行。?
觸發(fā)事件:指INSERT、DELETE或UPDATE事件,事件可以并行出現(xiàn),中間用OR連接。?
對(duì)于UPDATE事件,還可以用以下形式表示對(duì)某些列的修改會(huì)引起觸發(fā)器的動(dòng)作:?
UPDATE OF 列名1,列名2...?
ON 表名:表示為哪一個(gè)表創(chuàng)建觸發(fā)器。?
FOR EACH ROW:表示觸發(fā)器為行級(jí)觸發(fā)器,省略則為語(yǔ)句級(jí)觸發(fā)器。?
觸發(fā)器的創(chuàng)建者或具有DROP ANY TIRGGER系統(tǒng)權(quán)限的人才能刪除觸發(fā)器。刪除觸發(fā)器的語(yǔ)法如下:?
DROP TIRGGER 觸發(fā)器名?
可以通過(guò)命令設(shè)置觸發(fā)器的可用狀態(tài),使其暫時(shí)關(guān)閉或重新打開(kāi),即當(dāng)觸發(fā)器暫時(shí)不用時(shí),可以將其置成無(wú)效狀態(tài),在使用時(shí)重新打開(kāi)。該命令語(yǔ)法如下:?
ALTER TRIGGER 觸發(fā)器名 {DISABLE|ENABLE}?
其中,DISABLE表示使觸發(fā)器失效,ENABLE表示使觸發(fā)器生效。?
同存儲(chǔ)過(guò)程類似,觸發(fā)器可以用SHOW ERRORS 檢查編譯錯(cuò)誤。?
行級(jí)觸發(fā)器的應(yīng)用 ?
在行級(jí)觸發(fā)器中,SQL語(yǔ)句影響的每一行都會(huì)觸發(fā)一次觸發(fā)器,所以行級(jí)觸發(fā)器往往用在對(duì)表的每一行的操作進(jìn)行控制的場(chǎng)合。若在觸發(fā)器定義中出現(xiàn)FOR EACH ROW子句,則為語(yǔ)句級(jí)觸發(fā)器。?
【訓(xùn)練1】? 創(chuàng)建包含插入、刪除、修改多種觸發(fā)事件的觸發(fā)器DML_LOG,對(duì)EMP表的操作進(jìn)行記錄。用INSERTING、DELETING、UPDATING謂詞來(lái)區(qū)別不同的DML操作。?
在創(chuàng)建觸發(fā)器之前,需要先創(chuàng)建事件記錄表LOGS,該表用來(lái)對(duì)操作進(jìn)行記錄。該表的字段含義解釋如下:?
LOG_ID:操作記錄的編號(hào),數(shù)值型,它是該表的主鍵,由序列自動(dòng)生成。?
LOG_TABLE:進(jìn)行操作的表名,字符型,非空,該表設(shè)計(jì)成可以由多個(gè)觸發(fā)器共享使用。比如我們可以為dept表創(chuàng)建類似的觸發(fā)器,同樣將操作記錄到該表。?
LOG_DML:操作的動(dòng)作,即INSERT、DELETE或UPDATE三種之一。?
LOG_KEY_ID:操作時(shí)表的主鍵值,數(shù)值型。之所以記錄表的主鍵,是因?yàn)橹麈I是表的記錄的惟一標(biāo)識(shí),可以識(shí)別是對(duì)哪一條記錄進(jìn)行了操作。對(duì)于emp表,主鍵是empno。?
LOG_DATE:操作的日期,日期型,取當(dāng)前的系統(tǒng)時(shí)間。?
LOG_USER:操作者,字符型,取當(dāng)時(shí)的操作者賬戶名。比如登錄SCOTT賬戶進(jìn)行操作,在該字段中,記錄賬戶名為SCOTT。?
步驟1:在SQL*Plus中登錄STUDENT賬戶,創(chuàng)建如下的記錄表LOGS:?
Sql代碼?? CREATE?TABLE?logs(?? ????????LOG_ID?NUMBER(10)?PRIMARY?KEY,?? ????????LOG_TABLE?VARCHAR2(10)?NOT?NULL,?? ????????LOG_DML?VARCHAR2(10),?? ????????LOG_KEY_ID?NUMBER(10),?? ????????LOG_DATE?DATE,?? ????????LOG_USER?VARCHAR2(15)?? ????????);??
執(zhí)行結(jié)果:?
Sql代碼?? 表已創(chuàng)建。??
步驟2:創(chuàng)建一個(gè)LOGS表的主鍵序列LOGS_ID_SEQ:?
Sql代碼?? CREATE?SEQUENCE?logs_id_squ?INCREMENT?BY?1??? ????????START?WITH?1?MAXVALUE?9999999?NOCYCLE?NOCACHE;??
執(zhí)行結(jié)果:?
Sql代碼?? 序列已創(chuàng)建。??
步驟3:創(chuàng)建和編譯以下觸發(fā)器:?
Sql代碼?? CREATE?OR?REPLACE?TRIGGER?DML_LOG?? ????????BEFORE?--觸發(fā)時(shí)間為操作前?? ????????DELETE?OR?INSERT?OR?UPDATE?--?由三種事件觸發(fā)?? ????????ON?emp?? ????????FOR?EACH?ROW?--?行級(jí)觸發(fā)器?? ????????BEGIN?? IF?INSERTING?THEN??? ????????INSERT?INTO?logs????????VALUES(logs_id_squ.NEXTVAL,'EMP','INSERT',:new.empno,SYSDATE,USER);?? ?????????ELSIF?DELETING?THEN?? ?????????INSERT?INTO?logs???????VALUES(logs_id_squ.NEXTVAL,'EMP','DELETE',:old.empno,SYSDATE,USER);?? ?????????ELSE?? ?????????????INSERT?INTO?logs???????VALUES(logs_id_squ.NEXTVAL,'EMP','UPDATE',:new.empno,SYSDATE,USER);?? ????????END?IF;?? ????????END;??
執(zhí)行結(jié)果:?
觸發(fā)器已創(chuàng)建?
步驟4:在EMP表中插入記錄:?
Sql代碼?? INSERT?INTO?emp(empno,ename,job,sal)?VALUES(8001,'MARY','CLERK',1000);?? ????????COMMIT;??
執(zhí)行結(jié)果:?
Sql代碼?? 已創(chuàng)建1行。?? ????????提交完成。??
步驟5:檢查L(zhǎng)OGS表中記錄的信息:?
Sql代碼?? SELECT?*?FROM?LOGS;??
執(zhí)行結(jié)果為:?
??? Sql代碼?? LOG_ID?LOG_TABLE??LOG_DML????LOG_KEY_ID?LOG_DATE???LOG_USER?? ????????-----------------?-----------------?------------------?-----------------------?----------------?-------------------?? ??????????1?EMP?????????INSERT????????????8001????29-3月?-04?????STUDENT?? ????????已選擇?1?行。??
說(shuō)明:本例中在emp表上創(chuàng)建了一個(gè)由INSERT或DELETE或UPDATE事件觸發(fā)的行級(jí)觸發(fā)器,觸發(fā)器的名稱是LOG_EMP。對(duì)于不同的操作,記錄的內(nèi)容不同。本例中只插入了一條記錄,如果用一條不帶WHERE條件的UPDATE語(yǔ)句來(lái)修改所有雇員的工資,則將逐行觸發(fā)觸發(fā)器。?
INSERT、DELETE和UPDATE都能引發(fā)觸發(fā)器動(dòng)作,在分支語(yǔ)句中使用INSERTING、DELETING和UPDATING來(lái)區(qū)別是由哪種操作引發(fā)的觸發(fā)器動(dòng)作。?
在本例的插入動(dòng)作中,LOG_ID字段由序列LOG_ID_SQU自動(dòng)填充為1;LOGS表LOG_KEY_ID字段記錄的是新插入記錄的主鍵8001;LOD_DML字段記錄的是插入動(dòng)作INSERT;LOG_TABLE字段記錄當(dāng)前表名EMP;LOG_DATE字段記錄插入的時(shí)間04年3月1日;LOG_USER字段記錄插入者STUDENT。?
【練習(xí)1】修改、刪除剛剛插入的雇員記錄,提交后檢查L(zhǎng)OGS表的結(jié)果。?
【練習(xí)2】為DEPT表創(chuàng)建同樣的觸發(fā)器,使用LOGS表進(jìn)行記錄,并檢驗(yàn)結(jié)果。?
【訓(xùn)練2】? 創(chuàng)建一個(gè)行級(jí)觸發(fā)器LOG_SAL,記錄對(duì)職務(wù)為CLERK的雇員工資的修改,且當(dāng)修改幅度超過(guò)200時(shí)才進(jìn)行記錄。用WHEN條件限定觸發(fā)器。?
在創(chuàng)建觸發(fā)器之前,需要先創(chuàng)建事件記錄表LOGERR,該表用來(lái)對(duì)操作進(jìn)行記錄。該表的字段含義解釋如下:?
NUM:數(shù)值型,用于記錄序號(hào)。?
MESSAGE:字符型,用于記錄錯(cuò)誤信息。?
步驟1:在SQL*Plus中登錄STUDENT賬戶,創(chuàng)建如下的記錄表LOGERR:?
Sql代碼?? CREATE?TABLE?logerr(?? ????????NUM?NUMBER(10)?NOT?NULL,?? ????????MESSAGE?VARCHAR2(50)?NOT?NULL?? ????????);??
執(zhí)行結(jié)果:?
Sql代碼?? 表已創(chuàng)建。??
步驟2:創(chuàng)建和編譯以下觸發(fā)器:?
Sql代碼?? CREATE?OR?REPLACE?TRIGGER?log_sal?? ????????BEFORE?? ????????UPDATE?OF?sal?? ????????ON?emp?? ????????FOR?EACH?ROW?? ????????WHEN?(new.job='CLERK'?AND?(ABS(new.sal-old.sal)>200))??? DECLARE??? ????????v_no?NUMBER;?? ????????BEGIN?? ????????SELECT?COUNT(*)?INTO?v_no?FROM?logerr;??? ????????INSERT?INTO?logerr?VALUES(v_no+1,'雇員'||:new.ename||'的原工資:'||:old.sal||'新工資:'||:new.sal);?? ????????END;??
執(zhí)行結(jié)果:?
Sql代碼?? 觸發(fā)器已創(chuàng)建。??
步驟3:在EMP表中更新記錄:?
Sql代碼?? UPDATE?emp?SET?sal=sal+550?WHERE?empno=7788;?? ????????UPDATE?emp?SET?sal=sal+500?WHERE?empno=7369;?? ????????UPDATE?emp?SET?sal=sal+50?WHERE?empno=7876;?? ????????COMMIT;??
執(zhí)行結(jié)果:?
Sql代碼?? 已更新?1?行。?? ????????已更新?1?行。?? ????????已更新?1?行。?? ????????提交完成。??
步驟4:檢查L(zhǎng)OGSAL表中記錄的信息:?
Sql代碼?? SELECT?*?FROM?logerr;??
執(zhí)行結(jié)果為:
?????? Sql代碼?? NUM?MESSAGE?? ------------------?--------------------------------------------------------?? ?????????????1?雇員SMITH的原工資:800新工資:1300?? ????????已選擇?1?行。??
? 說(shuō)明:本例中,在emp表的sal列上創(chuàng)建了一個(gè)由UPDATE事件觸發(fā)的行級(jí)觸發(fā)器,觸發(fā)器的名稱是LOG_SAL。該觸發(fā)器由WHEN語(yǔ)句限定,只有當(dāng)被修改工資的雇員職務(wù)為CLERK,且修改的工資超過(guò)200時(shí)才進(jìn)行觸發(fā),否則不進(jìn)行觸發(fā)。?
所以在驗(yàn)證過(guò)程中,雖然修改了3條記錄,但通過(guò)查詢語(yǔ)句發(fā)現(xiàn):第一條修改語(yǔ)句修改編號(hào)為7788的SCOTT記錄,因?yàn)镾COTT的職務(wù)是ANALYST,不符合WHEN條件,沒(méi)有引起觸發(fā)器動(dòng)作;第二條修改語(yǔ)句修改編號(hào)為7369的SMITH的記錄,職務(wù)為CLERK,因?yàn)樵黾拥墓べY(500)超過(guò)了200,所以引起觸發(fā)器動(dòng)作,并在LOGERR表中進(jìn)行了記錄;第三條修改語(yǔ)句修改編號(hào)為7876的雇員ADAMS的記錄,雖然ADAMS的職務(wù)為CLERK,但修改的工資(50)沒(méi)有超過(guò)200,所以沒(méi)有引起觸發(fā)器動(dòng)作。?
注意:在WHEN條件中引用new和old不需要在前面加“: ”。?
在以上實(shí)例中,記錄了對(duì)工資的修改超出范圍的信息,但沒(méi)有限制對(duì)工資的修改。那么當(dāng)對(duì)雇員工資的修改幅度不滿足條件時(shí),能否直接限制對(duì)工資的修改呢?答案是肯定的。?
【訓(xùn)練3】? 創(chuàng)建觸發(fā)器CHECK_SAL,當(dāng)對(duì)職務(wù)為CLERK的雇員的工資修改超出500至2000的范圍時(shí),進(jìn)行限制。?
步驟1:創(chuàng)建和編譯以下觸發(fā)器:?
Sql代碼?? CREATE?OR?REPLACE?TRIGGER?CHECK_SAL?? ????????BEFORE?? ????????UPDATE?? ????????ON?emp?? ????????FOR?EACH?ROW?? ????????BEGIN?? IF?:new.job='CLERK'?AND?(:new.sal<500?OR?:new.sal>2000)?THEN?? ?????????RAISE_APPLICATION_ERROR(-20001,?'工資修改超出范圍,操作取消!');?? ????????END?IF;?? ????????END;??
執(zhí)行結(jié)果:?
Sql代碼?? 觸發(fā)器已創(chuàng)建。??
步驟2:在EMP表中插入記錄:?
Sql代碼?? UPDATE?emp?SET?sal=800?WHERE?empno=7876;?? ????????UPDATE?emp?SET?sal=450?WHERE?empno=7876;?? ????????COMMIT;??
執(zhí)行結(jié)果:?
Sql代碼?? UPDATE?emp?SET?sal=450?WHERE?empno=7876?? ????????????????*?? ????????ERROR?位于第?1?行:?? ????????ORA-20001:?工資修改超出范圍,操作取消!?? ????????ORA-06512:?在"STUDENT.CHECK_SAL",?line?3?? ????????ORA-04088:?觸發(fā)器?'STUDENT.CHECK_SAL'?執(zhí)行過(guò)程中出錯(cuò)提交完成。??
步驟3:檢查工資的修改結(jié)果:?
Sql代碼?? SELECT?empno,ename,job,sal?FROM?emp?WHERE?empno=7876;??
執(zhí)行結(jié)果為:?
??? Sql代碼?? EMPNO?ENAME??????JOB??????????????SAL?? -----------------?-------------?-------------?------------------------?? ????????7876??ADAMS??????CLERK????????????800??
說(shuō)明:在觸發(fā)器中,當(dāng)IF語(yǔ)句的條件滿足時(shí),即對(duì)職務(wù)為CLERK的雇員工資的修改超出指定范圍時(shí),用RAISE_APPLICATION_ERROR語(yǔ)句來(lái)定義一個(gè)臨時(shí)定義的異常,并立即引發(fā)異常。由于觸發(fā)器是BEFORE類型,因此觸發(fā)器先執(zhí)行,觸發(fā)器因異常而終止,SQL語(yǔ)句的執(zhí)行就會(huì)取消。?
通過(guò)步驟2的執(zhí)行信息可以看到,第一條語(yǔ)句修改編號(hào)為7876的雇員ADAMS的工資為800,成功執(zhí)行。第二條語(yǔ)句修改雇員ADAMS的工資為450,發(fā)生異常,執(zhí)行失敗。這樣就阻止了不符合條件的工資的修改。通過(guò)步驟3的查詢可以看到,雇員ADAMS最后的工資是800,即發(fā)生異常之前的修改結(jié)果。?
【練習(xí)3】限定對(duì)emp表的修改,只能修改部門(mén)10的雇員工資。?
?? 【訓(xùn)練4】? 創(chuàng)建一個(gè)行級(jí)觸發(fā)器CASCADE_UPDATE,當(dāng)修改部門(mén)編號(hào)時(shí),EMP表的相關(guān)行的部門(mén)編號(hào)也自動(dòng)修改。該觸發(fā)器稱為級(jí)聯(lián)修改觸發(fā)器。?
步驟1:創(chuàng)建和編譯以下觸發(fā)器:?
Sql代碼?? CREATE?TRIGGER?CASCADE_UPDATE?? AFTER??? UPDATE?OF?deptno?? ON?DEPT?? FOR?EACH?ROW?? BEGIN?? UPDATEEMP?SET?EMP.DEPTNO=:NEW.DEPTNO?? ?????????WHERE?EMP.DEPTNO=:OLD.DEPTNO;?? ????????END;??
執(zhí)行結(jié)果:?
Sql代碼?? 觸發(fā)器已創(chuàng)建??
步驟2:驗(yàn)證觸發(fā)器:?
Sql代碼?? UPDATE?dept?SET?deptno=11?WHERE?deptno=10;?? ????????COMMIT;??
執(zhí)行結(jié)果:?
Sql代碼?? 已更新?1?行。???
執(zhí)行查詢:?
Sql代碼?? SELECT?empno,ename,deptno?FROM?emp;??
執(zhí)行結(jié)果:?
?? Sql代碼?? ?EMPNO?ENAME??????????DEPTNO?? ????-----------------?-----------?-------------------------?? ?????????7369?SMITH?????????????????20?? ?????????7499?ALLEN?????????????????30?? ????????7521?WARD???????????????????30?? ????????7566?JONES??????????????????20?? 7654?MARTIN?????????????????30?? ????????7698?BLAKE??????????????????30?? ?????????7782?CLARK?????????????????11?? ?????????7839?KING??????????????????11?? ????????7844?TURNER?????????????????30?? ????????7876?ADAMS??????????????????20?? ????????7900?JAMES??????????????????30?? ?????????7902?FORD??????????????????20?? ?????????7934?MILLER????????????????11?? ????????7788?SCOTT??????????????????20??
說(shuō)明:通過(guò)檢查雇員的部門(mén)編號(hào),發(fā)現(xiàn)原來(lái)編號(hào)為10的部門(mén)編號(hào)被修改為11。?
本例中的UPDATE OF deptno表示只有在修改表的DEPTNO列時(shí)才引發(fā)觸發(fā)器,對(duì)其他列的修改不會(huì)引起觸發(fā)器的動(dòng)作。在觸發(fā)器中,對(duì)雇員表的部門(mén)編號(hào)與修改之前的部門(mén)編號(hào)一樣的雇員,修改其部門(mén)編號(hào)為新的部門(mén)編號(hào)。注意,在語(yǔ)句中同時(shí)用到了:new和:old來(lái)引用修改部門(mén)編號(hào)前后的部門(mén)編號(hào)。?
【練習(xí)4】建立級(jí)聯(lián)刪除觸發(fā)器CASCADE_DELETE,當(dāng)刪除部門(mén)時(shí),級(jí)聯(lián)刪除EMP表的雇員記錄。?
利用觸發(fā)器還可以修改數(shù)據(jù)。?
【訓(xùn)練5】? 將插入的雇員的名字變成以大寫(xiě)字母開(kāi)頭。?
步驟1:創(chuàng)建和編譯以下觸發(fā)器:?
Sql代碼?? CREATE?OR?REPLACE?TRIGGER?INITCAP?? BEFORE?INSERT??? ON?EMP?? FOR?EACH?ROW?? BEGIN?? ?:new.ename:=INITCAP(:new.ename);?? END;??
執(zhí)行結(jié)果:?
Sql代碼?? 觸發(fā)器已創(chuàng)建。??
步驟2:驗(yàn)證運(yùn)行結(jié)果:?
Sql代碼?? INSERT?INTO?emp(empno,ename,job,sal)?VALUES(1000,'BILL','CLERK',1500);??
執(zhí)行結(jié)果:?
Sql代碼?? 已創(chuàng)建?1?行。??
執(zhí)行查詢:?
Sql代碼?? SELECT?ename,job,sal?FROM?emp?WHERE?empno=1000;??
執(zhí)行結(jié)果:?
Sql代碼?? ENAME??????JOB??????????????SAL?? ????????-------------?-------------?------------------------?? ????????Bill??????????CLERK???????????1500??
說(shuō)明:在本例中,通過(guò)直接為:new.ename進(jìn)行賦值,修改了插入的值,但是這種用法只能在BEFORE型觸發(fā)器中使用。驗(yàn)證結(jié)果為,在插入語(yǔ)句中雇員名稱為大寫(xiě)的BILL,查詢結(jié)果中雇員名稱已經(jīng)轉(zhuǎn)換成以大寫(xiě)開(kāi)頭的Bill。?
【練習(xí)5】限定一次對(duì)雇員的工資修改不超過(guò)原工資的10%。?
語(yǔ)句級(jí)觸發(fā)器的應(yīng)用 ?
同行級(jí)觸發(fā)器不同,語(yǔ)句級(jí)觸發(fā)器的每個(gè)操作語(yǔ)句不管操作的行數(shù)是多少,只觸發(fā)一次觸發(fā)器,所以語(yǔ)句級(jí)觸發(fā)器適合于對(duì)整個(gè)表的操作權(quán)限等進(jìn)行控制。在觸發(fā)器定義中若省略FOR EACH ROW子句,則為語(yǔ)句級(jí)觸發(fā)器。?
【訓(xùn)練1】? 創(chuàng)建一個(gè)語(yǔ)句級(jí)觸發(fā)器CHECK_TIME,限定對(duì)表EMP的修改時(shí)間為周一至周五的早8點(diǎn)至晚5點(diǎn)。?
步驟1:創(chuàng)建和編譯以下觸發(fā)器:?
Sql代碼?? CREATE?OR?REPLACE?TRIGGER?CHECK_TIME?? BEFORE?? UPDATE?OR?INSERT?OR?DELETE??? ON?EMP?? BEGIN?? ?IF?(TO_CHAR(SYSDATE,'DY')?IN?('SAT','SUN'))??? ?OR?TO_CHAR(SYSDATE,'HH24')<?'08'??? ?OR?TO_CHAR(SYSDATE,'HH24')>='17'?THEN?? RAISE_APPLICATION_ERROR(-20500,'非法時(shí)間修改表錯(cuò)誤!');?? ?????????END?IF;??? ????????END;??
執(zhí)行結(jié)果:?
Sql代碼?? 觸發(fā)器已創(chuàng)建。??
步驟2:當(dāng)前時(shí)間為18點(diǎn)50分,在EMP表中插入記錄:?
Sql代碼?? UPDATE?EMP?SET?SAL=3000?WHERE?EMPNO=7369;??
顯示結(jié)果為:?
Sql代碼?? UPDATE?EMP?SET?SAL=3000?WHERE?EMPNO=7369?? ???????????????????????????????????????*?? ERROR?位于第?1?行:?? ????????ORA-20500:?非法時(shí)間修改表錯(cuò)誤!?? ????????ORA-06512:?在"STUDENT.CHECK_TIME",?line?5?? ????????ORA-04088:?觸發(fā)器?'STUDENT.CHECK_TIME'?執(zhí)行過(guò)程中出錯(cuò)??
? 說(shuō)明:通過(guò)引發(fā)異常限制對(duì)數(shù)據(jù)庫(kù)進(jìn)行的插入、刪除和修改操作的時(shí)間。SYSDATE用來(lái)獲取系統(tǒng)當(dāng)前時(shí)間,并按不同的格式字符串進(jìn)行轉(zhuǎn)換。“DY”表示獲取英文表示的星期簡(jiǎn)寫(xiě),“HH24”表示獲取24小時(shí)制時(shí)間的小時(shí)。?
當(dāng)在18點(diǎn)50分修改表中的數(shù)據(jù)時(shí),由于時(shí)間在8點(diǎn)至17點(diǎn)(晚5點(diǎn))之外,所以產(chǎn)生“非法時(shí)間修改表錯(cuò)誤”的用戶自定義錯(cuò)誤,修改操作終止。?
【練習(xí)1】設(shè)計(jì)一個(gè)語(yǔ)句級(jí)觸發(fā)器,限定只能對(duì)數(shù)據(jù)庫(kù)進(jìn)行修改操作,不能對(duì)數(shù)據(jù)庫(kù)進(jìn)行插入和刪除操作。在需要進(jìn)行插入和刪除時(shí),將觸發(fā)器設(shè)置為無(wú)效狀態(tài),完成后重新設(shè)置為生效狀態(tài)。?
數(shù)據(jù)庫(kù)事件觸發(fā)器 ?
數(shù)據(jù)庫(kù)事件觸發(fā)器有數(shù)據(jù)庫(kù)級(jí)和模式級(jí)兩種。前者定義在整個(gè)數(shù)據(jù)庫(kù)上,觸發(fā)事件是數(shù)據(jù)庫(kù)事件,如數(shù)據(jù)庫(kù)的啟動(dòng)、關(guān)閉,對(duì)數(shù)據(jù)庫(kù)的登錄或退出。后者定義在模式上,觸發(fā)事件包括模式用戶的登錄或退出,或?qū)?shù)據(jù)庫(kù)對(duì)象的創(chuàng)建和修改(DDL事件)。?
數(shù)據(jù)庫(kù)事件觸發(fā)器的觸發(fā)事件的種類和級(jí)別如表9-3所示。?
Sql代碼?? 種??類????關(guān)?鍵?字???說(shuō)????明?? 模式級(jí)?CREATE??在創(chuàng)建新對(duì)象時(shí)觸發(fā)?? ????ALTER???修改數(shù)據(jù)庫(kù)或數(shù)據(jù)庫(kù)對(duì)象時(shí)觸發(fā)?? ????DROP????刪除對(duì)象時(shí)觸發(fā)?? 數(shù)據(jù)庫(kù)級(jí)????STARTUP?數(shù)據(jù)庫(kù)打開(kāi)時(shí)觸發(fā)?? ????SHUTDOWN????在使用NORMAL或IMMEDIATE選項(xiàng)關(guān)閉數(shù)據(jù)庫(kù)時(shí)觸發(fā)?? ????SERVERERROR?????發(fā)生服務(wù)器錯(cuò)誤時(shí)觸發(fā)?? 數(shù)據(jù)庫(kù)級(jí)與模式級(jí)????LOGON???當(dāng)用戶連接到數(shù)據(jù)庫(kù),建立會(huì)話時(shí)觸發(fā)?? ????LOGOFF??當(dāng)會(huì)話從數(shù)據(jù)庫(kù)中斷開(kāi)時(shí)觸發(fā)??
定義數(shù)據(jù)庫(kù)事件和模式事件觸發(fā)器 ?
創(chuàng)建數(shù)據(jù)庫(kù)級(jí)觸發(fā)器需要ADMINISTER DATABASE TRIGGER系統(tǒng)權(quán)限,一般只有系統(tǒng)管理員擁有該權(quán)限。?
對(duì)于模式級(jí)觸發(fā)器,為自己的模式創(chuàng)建觸發(fā)器需要CREATE TRIGGER權(quán)限,如果是為其他模式創(chuàng)建觸發(fā)器,需要CREATE ANY TRIGGER權(quán)限。?
數(shù)據(jù)庫(kù)事件和模式事件觸發(fā)器的創(chuàng)建語(yǔ)法與DML觸發(fā)器的創(chuàng)建語(yǔ)法類似。數(shù)據(jù)庫(kù)事件或模式事件觸發(fā)器的創(chuàng)建語(yǔ)法如下:?
CREATE [OR REPLACE] TRIGGER 觸發(fā)器名?
{BEFORE|AFTER }?
{DDL事件1 [DDL事件2...]| 數(shù)據(jù)庫(kù)事件1 [數(shù)據(jù)庫(kù)事件2...]}?
ON {DATABASE| [模式名.]SCHEMA }?
[WHEN (條件)]?
DECLARE?
聲明部分?
BEGIN?
主體部分?
END;?
其中:DATABASE表示創(chuàng)建數(shù)據(jù)庫(kù)級(jí)觸發(fā)器,數(shù)據(jù)庫(kù)級(jí)要給出數(shù)據(jù)庫(kù)事件;SCHEMA表示創(chuàng)建模式級(jí)觸發(fā)器,模式級(jí)要給出模式事件或DDL事件。?
在數(shù)據(jù)庫(kù)事件觸發(fā)器中,可以使用如表9-4所示的一些事件屬性。不同類型的觸發(fā)器可以使用的事件屬性有所不同。?
Sql代碼?? 屬??性????適用觸發(fā)器類型?說(shuō)????明?? Sys.sysevent????所有類型????返回觸發(fā)器觸發(fā)事件字符串?? Sys.instance_num????所有類型????返回Oracle實(shí)例號(hào)?? Sys.database_name???所有類型????返回?cái)?shù)據(jù)庫(kù)名字?? Sys.server_error(stack_position)????SERVERERROR?從錯(cuò)誤堆棧指定位置返回錯(cuò)誤號(hào),參數(shù)為1表示最近的錯(cuò)誤?? Is_servererror(error_number)????SERVERERROR?判斷堆棧中是否有參數(shù)指定的錯(cuò)誤號(hào)?? Sys.login_user??所有類型????返回導(dǎo)致觸發(fā)器觸發(fā)的用戶名?? Sys.dictionary_obj_type?CREATE、ALTER、DROP???返回DDL觸發(fā)器觸發(fā)時(shí)涉及的對(duì)象類型?? Sys.?dictionary_obj_name????CREATE、ALTER、DROP???返回DDL觸發(fā)器觸發(fā)時(shí)涉及的對(duì)象名稱?? Sys.des_encrypted_password??CREATE、ALTER、DROP???創(chuàng)建或修改用戶時(shí),返回加密后的用戶密碼??
數(shù)據(jù)庫(kù)事件觸發(fā)器 ?
下面是一個(gè)綜合的數(shù)據(jù)庫(kù)事件觸發(fā)器練習(xí)。先為STUDENT賬戶授予創(chuàng)建數(shù)據(jù)庫(kù)事件觸發(fā)器的權(quán)限,ADMINISTER DATABASE TRIGGER,然后創(chuàng)建有關(guān)的表和觸發(fā)器,最后予以驗(yàn)證。?
? 【訓(xùn)練1】? 創(chuàng)建觸發(fā)器,對(duì)本次數(shù)據(jù)庫(kù)啟動(dòng)以來(lái)的用戶登錄時(shí)間進(jìn)行記錄,每次數(shù)據(jù)庫(kù)啟動(dòng)后,先清空該表。?
步驟1:創(chuàng)建登錄事件記錄表:?
Sql代碼?? CREATE?TABLE?userlog?(?? USERNAME?VARCHAR2(20),?? LOGON_TIME?DATE);??
執(zhí)行結(jié)果:?
Sql代碼?? 表已創(chuàng)建。??
步驟2:創(chuàng)建數(shù)據(jù)庫(kù)STARTUP事件觸發(fā)器:?
Sql代碼?? CREATE?OR?REPLACE?TRIGGER?INIT_LOGON?? AFTER?? STARTUP??? ON?DATABASE?? BEGIN?? ?DELETE?FROM?userlog;?? END;??
執(zhí)行結(jié)果:?
Sql代碼?? 觸發(fā)器已創(chuàng)建。??
步驟3:創(chuàng)建數(shù)據(jù)庫(kù)LOGON事件觸發(fā)器:?
Sql代碼?? CREATE?OR?REPLACE?TRIGGER?DATABASE_LOGON?? AFTER?? LOGON??? ON?DATABASE?? BEGIN?? ?INSERT?INTO?userlog??? ?VALUES(sys.login_user,sysdate);?? END;??
執(zhí)行結(jié)果:?
Sql代碼?? 觸發(fā)器已創(chuàng)建。??
步驟4:驗(yàn)證DATABASE_LOGON觸發(fā)器:?
Sql代碼?? CONNECT?SCOTT/TIGER@MYDB;?? ????????CONNECT?STUDENT/STUDENT@MYDB;??
執(zhí)行結(jié)果:?
Sql代碼?? 已連接。?? 已連接。??
執(zhí)行查詢:?
Sql代碼?? SELECT?username,TO_CHAR(logon_time,'YYYY/MM/DD?HH24:MI:SS')?FROM?userlog;??
執(zhí)行結(jié)果:?
Sql代碼?? USERNAME?????????????TO_CHAR(LOGON_TIME,?? -----------------------------?-----------------------------------------?? SCOTT???????????????????2004/03/29?22:42:20?? ????????STUDENT?????????????????2004/03/29?22:42:20??
步驟5:驗(yàn)證INIT_LOGON觸發(fā)器。?
重新啟動(dòng)數(shù)據(jù)庫(kù),登錄STUDENT賬戶:?
Sql代碼?? SELECT?username,TO_CHAR(logon_time,'YYYY/MM/DD?HH24:MI:SS')?FROM?userlog;??
執(zhí)行結(jié)果:?
Sql代碼?? USERNAME?????????????TO_CHAR(LOGON_TIME,?? --------------------------------?---------------------------------------?? ????????STUDENT??????????????2004/03/29?22:43:59?? ????????已選擇?1?行??
說(shuō)明:本例中共創(chuàng)建了兩個(gè)數(shù)據(jù)庫(kù)級(jí)事件觸發(fā)器。DATABASE_LOGON在用戶登錄時(shí)觸發(fā),向表userlog中增加一條記錄,記錄登錄用戶名和登錄時(shí)間。INIT_LOGON在數(shù)據(jù)庫(kù)啟動(dòng)時(shí)觸發(fā),清除userlog表中記錄的數(shù)據(jù)。所以當(dāng)數(shù)據(jù)庫(kù)重新啟動(dòng)后,重新登錄STUDENT賬戶,此時(shí)userlog表中只有一條記錄。?
【訓(xùn)練2】? 創(chuàng)建STUDENT_LOGON模式級(jí)觸發(fā)器,專門(mén)記錄STUDENT賬戶的登錄時(shí)間:?
Sql代碼?? CREATE?OR?REPLACE?TRIGGER?STUDENT_LOGON??? AFTER?? LOGON?ON?STUDENT.SCHEMA??? BEGIN???? ?INSERT?INTO?userlog?? ?VALUES(sys.login_user,sysdate);?? END;??
執(zhí)行結(jié)果:?
Sql代碼?? 觸發(fā)器已創(chuàng)建。??
說(shuō)明:為當(dāng)前模式創(chuàng)建觸發(fā)器,可以省略SCHEMA前面的模式名。?
【練習(xí)1】修改DATABASE_LOGON觸發(fā)器和userlog表,增加對(duì)退出時(shí)間的記錄。?
DDL事件觸發(fā)器 ?
【訓(xùn)練1】? 通過(guò)觸發(fā)器阻止對(duì)emp表的刪除。?
步驟1:創(chuàng)建DDL觸發(fā)器:?
Sql代碼?? CREATE?OR?REPLACE?TRIGGER?NODROP_EMP?? ?????????BEFORE?? ????????DROP?ON?SCHEMA??? ????????BEGIN?? ????????IF?Sys.Dictionary_obj_name='EMP'?THEN?? RAISE_APPLICATION_ERROR(-20005,'錯(cuò)誤信息:不能刪除emp表!');?? ?????????END?IF;??? ????????END;??
執(zhí)行結(jié)果:?
Sql代碼?? 觸發(fā)器已創(chuàng)建。??
步驟2:通過(guò)刪除emp表驗(yàn)證觸發(fā)器:?
Sql代碼?? DROP?TABLE?emp;??
執(zhí)行結(jié)果:?
Sql代碼?? DROP?TABLE?emp?? ????????*?? ERROR?位于第?1?行:?? ????????ORA-00604:?遞歸?SQL?層?1?出現(xiàn)錯(cuò)誤?? ????????ORA-20005:?錯(cuò)誤信息:不能刪除emp表!?? ????????ORA-06512:?在line?3??
? ?? 說(shuō)明:該觸發(fā)器阻止在當(dāng)前模式下對(duì)emp表的刪除,但不阻止刪除其他對(duì)象。Sys.Dictionary_obj_name屬性返回要?jiǎng)h除的對(duì)象名稱。?
替代觸發(fā)器 ?
【訓(xùn)練1】? 在emp表的視圖上,通過(guò)觸發(fā)器修改emp表。?
步驟1:創(chuàng)建視圖emp_name:?
Sql代碼?? CREATE?VIEW?emp_name?AS?SELECT?ename?FROM?emp;??
執(zhí)行結(jié)果:?
Sql代碼?? 視圖已建立。??
步驟1:創(chuàng)建替代觸發(fā)器:?
Sql代碼?? CREATE?OR?REPLACE?TRIGGER?change_name?? ????????INSTEAD?OF?INSERT?ON?emp_name?? ????????DECLARE?? V_EMPNO?NUMBER(4);?? ????????BEGIN?? ????????SELECT?MAX(EMPNO)+1?INTO?V_EMPNO?FROM?EMP;?? ????????INSERT?INTO?emp(empno,ename)??? ????????VALUES(V_EMPNO,:new.ename);?? ????????END;??
執(zhí)行結(jié)果:?
Sql代碼?? 觸發(fā)器已創(chuàng)建。??
步驟2:向emp_name視圖插入記錄:?
Sql代碼?? INSERT?INTO?emp_name?VALUES('BROWN');?? ????????COMMIT;??
執(zhí)行結(jié)果:?
Sql代碼?? 已創(chuàng)建?1?行。?? ????????提交完成。??
? 說(shuō)明:向視圖直接插入雇員名將會(huì)發(fā)生錯(cuò)誤,因?yàn)閑mp表的雇員編號(hào)列不允許為空。通過(guò)創(chuàng)建替代觸發(fā)器,將向視圖插入雇員名稱轉(zhuǎn)換為向emp表插入雇員編號(hào)和雇員名稱,雇員編號(hào)取當(dāng)前的最大雇員編號(hào)加1。試檢查emp表的雇員列表。?
【訓(xùn)練2】? 在emp表的視圖emp_name上,通過(guò)觸發(fā)器阻止對(duì)emp表的刪除。?
步驟1:阻止通過(guò)視圖刪除雇員,并顯示用戶自定義錯(cuò)誤信息:?
Sql代碼?? CREATE?OR?REPLACE?TRIGGER?delete_from_ename?? ????????INSTEAD?OF?DELETE?ON?emp_name?? ????????BEGIN?? ?????????RAISE_APPLICATION_ERROR(-20006,'錯(cuò)誤信息:不能在視圖中刪除emp表的雇員!');?? ????????END;??
執(zhí)行結(jié)果:?
Sql代碼?? 觸發(fā)器已創(chuàng)建。??
步驟2:通過(guò)對(duì)視圖進(jìn)行刪除來(lái)驗(yàn)證觸發(fā)器:?
Sql代碼?? DELETE?FROM?emp_name;??
執(zhí)行結(jié)果:?
Sql代碼?? DELETE?FROM?emp_name?? ????????????????*?? ????????ERROR?位于第?1?行:?? ????????ORA-20006:?錯(cuò)誤信息:不能在視圖中刪除emp表的雇員!?? ????????ORA-06512:??? ????????在"STUDENT.DELETE_FROM_ENAME",?line?2?? ????????ORA-04088:?觸發(fā)器?'STUDENT.DELETE_FROM_ENAME'?執(zhí)行過(guò)程中出錯(cuò)??
說(shuō)明:可以通過(guò)視圖emp_name對(duì)雇員進(jìn)行刪除,比如執(zhí)行DELETE FROM emp_name語(yǔ)句將刪除雇員表的全部雇員。但是由于在emp_name視圖中只能看到一部分雇員信息,所以刪除可能會(huì)產(chǎn)生誤操作。通過(guò)定義一個(gè)替代觸發(fā)器,可阻止通過(guò)emp_name視圖對(duì)emp表雇員進(jìn)行刪除,但不阻止直接對(duì)emp表進(jìn)行刪除。?
查看觸發(fā)器 ?
【訓(xùn)練1】? 顯示觸發(fā)器CHECK_TIME的體部分:?
Sql代碼?? SELECT?TRIGGER_BODY?FROM?USER_TRIGGERS?WHERE?TRIGGER_NAME='CHECK_TIME';??
結(jié)果為:?
Sql代碼?? TRIGGER_BODY?? ????????----------------------------------------------------------------------------------------?? ????????BEGIN?? ????????IF?(TO_CHAR(SYSDATE,'DY')?IN?('SAT','SUN'))?? ?????????OR?TO_CHAR(SYSDATE,'HH24')<?? ???????TRIGGER_BODY字段為L(zhǎng)ONG類型,只顯示出腳本的一部分內(nèi)容。??
階段訓(xùn)練 ?
【訓(xùn)練1】? 創(chuàng)建觸發(fā)器,進(jìn)行表的同步復(fù)制。?
步驟1:創(chuàng)建emp表的復(fù)本employee:?
Sql代碼?? CREATE?TABLE?employee?AS?SELECT?*?FROM?emp;??
執(zhí)行結(jié)果:?
Sql代碼?? 表已創(chuàng)建。???
步驟2:創(chuàng)建和編譯以下觸發(fā)器:?
Sql代碼?? CREATE?OR?REPLACE?TRIGGER?DUPLICATE_EMP?? ????????AFTER??? ????????UPDATE?OR?INSERT?OR?DELETE?? ????????ON?EMP?? ????????FOR?EACH?ROW?? ????????BEGIN????? ?????????IF?INSERTING?THEN??? ?????????INSERT?INTO?employee??? ?????????VALUES(:new.empno,:new.ename,:new.job,:new.mgr,?? ???????????:new.hiredate,:new.sal,:new.comm,:new.deptno);?? ELSIF?DELETING?THEN?? ??DELETE?FROM?employee??? ??WHERE?empno=:old.empno;?? ?ELSE?? ??UPDATE?employee?SET??? ??empno=:new.empno,?? ??ename=:new.ename,?? ??job=:new.job,?? mgr=:new.mgr,?? ??hiredate=:new.hiredate,?? ??sal=:new.sal,?? ??comm=:new.comm,?? ??deptno=:new.deptno?? ??WHERE?empno=:old.empno;?? ?END?IF;?? END;??
執(zhí)行結(jié)果:?
Sql代碼?? 觸發(fā)器已創(chuàng)建。??
步驟3:對(duì)emp表進(jìn)行插入、刪除和更新:?
Sql代碼?? DELETE?FROM?emp?WHERE?empno=7934;?? ????????INSERT?INTO?emp(empno,ename,job,sal)????????VALUES(8888,'ROBERT','ANALYST',2900);?? ????????UPDATE?emp?SET?sal=3900?WHERE?empno=7788;?? ????????COMMIT;??
執(zhí)行結(jié)果:?
Sql代碼?? 已刪除?1?行。?? 已創(chuàng)建?1?行。?? 已更新?1?行。?? 提交完成。???
步驟4:檢查emp表和employee表中被插入、刪除和更新的雇員。?
運(yùn)行結(jié)果略,請(qǐng)自行驗(yàn)證。?
? 說(shuō)明:在觸發(fā)器中判斷觸發(fā)事件,根據(jù)不同的事件對(duì)employee表進(jìn)行不同的操作。?
【練習(xí)1】創(chuàng)建一個(gè)emp表的觸發(fā)器EMP_TOTAL,每當(dāng)向雇員表插入、刪除或更新雇員信息時(shí),將新的統(tǒng)計(jì)信息存入統(tǒng)計(jì)表EMPTOTAL,使統(tǒng)計(jì)表總能夠反映最新的統(tǒng)計(jì)信息。?
統(tǒng)計(jì)表是記錄各部門(mén)雇員總?cè)藬?shù)、總工資的統(tǒng)計(jì)表,結(jié)構(gòu)如下:?
部門(mén)編號(hào) number(2)?
總?cè)藬?shù) number(5)?
總工資 number(10,2)?
練習(xí) ?
1. 下列有關(guān)觸發(fā)器和存儲(chǔ)過(guò)程的描述,正確的是:?
A. 兩者都可以傳遞參數(shù)?
B. 兩者都可以被其他程序調(diào)用?
C. 兩種模塊中都可以包含數(shù)據(jù)庫(kù)事務(wù)語(yǔ)句?
D. 兩者創(chuàng)建的系統(tǒng)權(quán)限不同?
2. 下列事件,屬于DDL事件的是:?
??? A.? INSERT B.? LOGON?
??? C.? DROP D.? SERVERERROR?
3. 假定在一個(gè)表上同時(shí)定義了行級(jí)和語(yǔ)句級(jí)觸發(fā)器,在一次觸發(fā)當(dāng)中,下列說(shuō)法正確的是:?
??? A. 語(yǔ)句級(jí)觸發(fā)器只執(zhí)行一次?
??? B. 語(yǔ)句級(jí)觸發(fā)器先于行級(jí)觸發(fā)器執(zhí)行?
?????????? C. 行級(jí)觸發(fā)器先于語(yǔ)句級(jí)觸發(fā)器執(zhí)行?
??????????????? D. 行級(jí)觸發(fā)器對(duì)表的每一行都會(huì)執(zhí)行一次?
4. 有關(guān)行級(jí)觸發(fā)器的偽記錄,下列說(shuō)法正確的是:?
?? A.? INSERT事件觸發(fā)器中,可以使用:old偽記錄。?
?? B.? DELETE事件觸發(fā)器中,可以使用:new偽記錄。?
?? C.? UPDATA事件觸發(fā)器中,只能使用:new偽記錄。?
??????????????? D.? UPDATA事件觸發(fā)器中,可以使用:old偽記錄。?
5. 下列有關(guān)替代觸發(fā)器的描述,正確的是:?
??? A. 替代觸發(fā)器創(chuàng)建在表上?
??????????????? B. 替代觸發(fā)器創(chuàng)建在數(shù)據(jù)庫(kù)上?
??????????????? C. 通過(guò)替代觸發(fā)器可以向基表插入數(shù)據(jù)?
??????????????? D. 通過(guò)替代觸發(fā)器可以向視圖插入數(shù)據(jù)?
觸發(fā)器必須由事件才能觸發(fā)。觸發(fā)器的觸發(fā)事件分可為3類,分別是DML事件、DDL事件和數(shù)據(jù)庫(kù)事件。?
每類事件包含若干個(gè)事件,如下所示。數(shù)據(jù)庫(kù)的事件是具體的,在創(chuàng)建觸發(fā)器時(shí)要指明觸發(fā)的事件。?
種? 類 關(guān) 鍵 字 含??? 義?
Sql代碼??
觸發(fā)器的類型可劃分為4種:數(shù)據(jù)操縱語(yǔ)言(DML)觸發(fā)器、替代(INSTEAD OF)觸發(fā)器、數(shù)據(jù)定義語(yǔ)言(DDL)觸發(fā)器和數(shù)據(jù)庫(kù)事件觸發(fā)器。?
各類觸發(fā)器的作用如下所示。?
Sql代碼??
DML觸發(fā)器的要點(diǎn) ?
DML觸發(fā)器是定義在表上的觸發(fā)器,由DML事件引發(fā)。編寫(xiě)DML觸發(fā)器的要素是:?
* 確定觸發(fā)的表,即在其上定義觸發(fā)器的表。?
* 確定觸發(fā)的事件,DML觸發(fā)器的觸發(fā)事件有INSERT、UPDATE和DELETE三種,說(shuō)明見(jiàn)下。?
* 確定觸發(fā)時(shí)間。觸發(fā)的時(shí)間有BEFORE和AFTER兩種,分別表示觸發(fā)動(dòng)作發(fā)生在DML語(yǔ)句執(zhí)行之前和語(yǔ)句執(zhí)行之后。?
* 確定觸發(fā)級(jí)別,有語(yǔ)句級(jí)觸發(fā)器和行級(jí)觸發(fā)器兩種。語(yǔ)句級(jí)觸發(fā)器表示SQL語(yǔ)句只觸發(fā)一次觸發(fā)器,行級(jí)觸發(fā)器表示SQL語(yǔ)句影響的每一行都要觸發(fā)一次。?
由于在同一個(gè)表上可以定義多個(gè)DML觸發(fā)器,因此觸發(fā)器本身和引發(fā)觸發(fā)器的SQL語(yǔ)句在執(zhí)行的順序上有先后的關(guān)系。它們的順序是:?
* 如果存在語(yǔ)句級(jí)BEFORE觸發(fā)器,則先執(zhí)行一次語(yǔ)句級(jí)BEFORE觸發(fā)器。?
* 在SQL語(yǔ)句的執(zhí)行過(guò)程中,如果存在行級(jí)BEFORE觸發(fā)器,則SQL語(yǔ)句在對(duì)每一行操作之前,都要先執(zhí)行一次行級(jí)BEFORE觸發(fā)器,然后才對(duì)行進(jìn)行操作。如果存在行級(jí)AFTER觸發(fā)器,則SQL語(yǔ)句在對(duì)每一行操作之后,都要再執(zhí)行一次行級(jí)AFTER觸發(fā)器。?
* 如果存在語(yǔ)句級(jí)AFTER觸發(fā)器,則在SQL語(yǔ)句執(zhí)行完畢后,要最后執(zhí)行一次語(yǔ)句級(jí)AFTER觸發(fā)器。?
DML觸發(fā)器還有一些具體的問(wèn)題,說(shuō)明如下:?
* 如果有多個(gè)觸發(fā)器被定義成為相同時(shí)間、相同事件觸發(fā),且最后定義的觸發(fā)器是有效的,則最后定義的觸發(fā)器被觸發(fā),其他觸發(fā)器不執(zhí)行。?
* 一個(gè)觸發(fā)器可由多個(gè)不同的DML操作觸發(fā)。在觸發(fā)器中,可用INSERTING、DELETING、UPDATING謂詞來(lái)區(qū)別不同的DML操作。這些謂詞可以在IF分支條件語(yǔ)句中作為判斷條件來(lái)使用。?
* 在行級(jí)觸發(fā)器中,用:new 和:old(稱為偽記錄)來(lái)訪問(wèn)數(shù)據(jù)變更前后的值。但要注意,INSERT語(yǔ)句插入一條新記錄,所以沒(méi)有:old記錄,而DELETE語(yǔ)句刪除掉一條已經(jīng)存在的記錄,所以沒(méi)有:new記錄。UPDATE語(yǔ)句既有:old記錄,也有:new記錄,分別代表修改前后的記錄。引用具體的某一列的值的方法是:?
??????? ld.字段名或:new.字段名?
* 觸發(fā)器體內(nèi)禁止使用COMMIT、ROLLBACK、SAVEPOINT語(yǔ)句,也禁止直接或間接地調(diào)用含有上述語(yǔ)句的存儲(chǔ)過(guò)程。?
定義一個(gè)觸發(fā)器時(shí)要考慮上述多種情況,并根據(jù)具體的需要來(lái)決定觸發(fā)器的種類。?
DML觸發(fā)器的創(chuàng)建 ?
創(chuàng)建DML觸發(fā)器需要CREATE TRIGGER系統(tǒng)權(quán)限。創(chuàng)建DML觸發(fā)器的語(yǔ)法如下:?
CREATE [OR REPLACE] TRIGGER 觸發(fā)器名?
{BEFORE|AFTER|INSTEAD OF} 觸發(fā)事件1 [OR 觸發(fā)事件2...]?
ON 表名?
WHEN 觸發(fā)條件?
[FOR EACH ROW]?
DECLARE?
聲明部分?
BEGIN?
主體部分?
END;?
其中:?
OR REPLACE:表示如果存在同名觸發(fā)器,則覆蓋原有同名觸發(fā)器。?
BEFORE、AFTER和INSTEAD OF:說(shuō)明觸發(fā)器的類型。?
WHEN 觸發(fā)條件:表示當(dāng)該條件滿足時(shí),觸發(fā)器才能執(zhí)行。?
觸發(fā)事件:指INSERT、DELETE或UPDATE事件,事件可以并行出現(xiàn),中間用OR連接。?
對(duì)于UPDATE事件,還可以用以下形式表示對(duì)某些列的修改會(huì)引起觸發(fā)器的動(dòng)作:?
UPDATE OF 列名1,列名2...?
ON 表名:表示為哪一個(gè)表創(chuàng)建觸發(fā)器。?
FOR EACH ROW:表示觸發(fā)器為行級(jí)觸發(fā)器,省略則為語(yǔ)句級(jí)觸發(fā)器。?
觸發(fā)器的創(chuàng)建者或具有DROP ANY TIRGGER系統(tǒng)權(quán)限的人才能刪除觸發(fā)器。刪除觸發(fā)器的語(yǔ)法如下:?
DROP TIRGGER 觸發(fā)器名?
可以通過(guò)命令設(shè)置觸發(fā)器的可用狀態(tài),使其暫時(shí)關(guān)閉或重新打開(kāi),即當(dāng)觸發(fā)器暫時(shí)不用時(shí),可以將其置成無(wú)效狀態(tài),在使用時(shí)重新打開(kāi)。該命令語(yǔ)法如下:?
ALTER TRIGGER 觸發(fā)器名 {DISABLE|ENABLE}?
其中,DISABLE表示使觸發(fā)器失效,ENABLE表示使觸發(fā)器生效。?
同存儲(chǔ)過(guò)程類似,觸發(fā)器可以用SHOW ERRORS 檢查編譯錯(cuò)誤。?
行級(jí)觸發(fā)器的應(yīng)用 ?
在行級(jí)觸發(fā)器中,SQL語(yǔ)句影響的每一行都會(huì)觸發(fā)一次觸發(fā)器,所以行級(jí)觸發(fā)器往往用在對(duì)表的每一行的操作進(jìn)行控制的場(chǎng)合。若在觸發(fā)器定義中出現(xiàn)FOR EACH ROW子句,則為語(yǔ)句級(jí)觸發(fā)器。?
【訓(xùn)練1】? 創(chuàng)建包含插入、刪除、修改多種觸發(fā)事件的觸發(fā)器DML_LOG,對(duì)EMP表的操作進(jìn)行記錄。用INSERTING、DELETING、UPDATING謂詞來(lái)區(qū)別不同的DML操作。?
在創(chuàng)建觸發(fā)器之前,需要先創(chuàng)建事件記錄表LOGS,該表用來(lái)對(duì)操作進(jìn)行記錄。該表的字段含義解釋如下:?
LOG_ID:操作記錄的編號(hào),數(shù)值型,它是該表的主鍵,由序列自動(dòng)生成。?
LOG_TABLE:進(jìn)行操作的表名,字符型,非空,該表設(shè)計(jì)成可以由多個(gè)觸發(fā)器共享使用。比如我們可以為dept表創(chuàng)建類似的觸發(fā)器,同樣將操作記錄到該表。?
LOG_DML:操作的動(dòng)作,即INSERT、DELETE或UPDATE三種之一。?
LOG_KEY_ID:操作時(shí)表的主鍵值,數(shù)值型。之所以記錄表的主鍵,是因?yàn)橹麈I是表的記錄的惟一標(biāo)識(shí),可以識(shí)別是對(duì)哪一條記錄進(jìn)行了操作。對(duì)于emp表,主鍵是empno。?
LOG_DATE:操作的日期,日期型,取當(dāng)前的系統(tǒng)時(shí)間。?
LOG_USER:操作者,字符型,取當(dāng)時(shí)的操作者賬戶名。比如登錄SCOTT賬戶進(jìn)行操作,在該字段中,記錄賬戶名為SCOTT。?
步驟1:在SQL*Plus中登錄STUDENT賬戶,創(chuàng)建如下的記錄表LOGS:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
步驟2:創(chuàng)建一個(gè)LOGS表的主鍵序列LOGS_ID_SEQ:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
步驟3:創(chuàng)建和編譯以下觸發(fā)器:?
Sql代碼??
執(zhí)行結(jié)果:?
觸發(fā)器已創(chuàng)建?
步驟4:在EMP表中插入記錄:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
步驟5:檢查L(zhǎng)OGS表中記錄的信息:?
Sql代碼??
執(zhí)行結(jié)果為:?
??? Sql代碼??
說(shuō)明:本例中在emp表上創(chuàng)建了一個(gè)由INSERT或DELETE或UPDATE事件觸發(fā)的行級(jí)觸發(fā)器,觸發(fā)器的名稱是LOG_EMP。對(duì)于不同的操作,記錄的內(nèi)容不同。本例中只插入了一條記錄,如果用一條不帶WHERE條件的UPDATE語(yǔ)句來(lái)修改所有雇員的工資,則將逐行觸發(fā)觸發(fā)器。?
INSERT、DELETE和UPDATE都能引發(fā)觸發(fā)器動(dòng)作,在分支語(yǔ)句中使用INSERTING、DELETING和UPDATING來(lái)區(qū)別是由哪種操作引發(fā)的觸發(fā)器動(dòng)作。?
在本例的插入動(dòng)作中,LOG_ID字段由序列LOG_ID_SQU自動(dòng)填充為1;LOGS表LOG_KEY_ID字段記錄的是新插入記錄的主鍵8001;LOD_DML字段記錄的是插入動(dòng)作INSERT;LOG_TABLE字段記錄當(dāng)前表名EMP;LOG_DATE字段記錄插入的時(shí)間04年3月1日;LOG_USER字段記錄插入者STUDENT。?
【練習(xí)1】修改、刪除剛剛插入的雇員記錄,提交后檢查L(zhǎng)OGS表的結(jié)果。?
【練習(xí)2】為DEPT表創(chuàng)建同樣的觸發(fā)器,使用LOGS表進(jìn)行記錄,并檢驗(yàn)結(jié)果。?
【訓(xùn)練2】? 創(chuàng)建一個(gè)行級(jí)觸發(fā)器LOG_SAL,記錄對(duì)職務(wù)為CLERK的雇員工資的修改,且當(dāng)修改幅度超過(guò)200時(shí)才進(jìn)行記錄。用WHEN條件限定觸發(fā)器。?
在創(chuàng)建觸發(fā)器之前,需要先創(chuàng)建事件記錄表LOGERR,該表用來(lái)對(duì)操作進(jìn)行記錄。該表的字段含義解釋如下:?
NUM:數(shù)值型,用于記錄序號(hào)。?
MESSAGE:字符型,用于記錄錯(cuò)誤信息。?
步驟1:在SQL*Plus中登錄STUDENT賬戶,創(chuàng)建如下的記錄表LOGERR:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
步驟2:創(chuàng)建和編譯以下觸發(fā)器:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
步驟3:在EMP表中更新記錄:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
步驟4:檢查L(zhǎng)OGSAL表中記錄的信息:?
Sql代碼??
執(zhí)行結(jié)果為:
?????? Sql代碼??
? 說(shuō)明:本例中,在emp表的sal列上創(chuàng)建了一個(gè)由UPDATE事件觸發(fā)的行級(jí)觸發(fā)器,觸發(fā)器的名稱是LOG_SAL。該觸發(fā)器由WHEN語(yǔ)句限定,只有當(dāng)被修改工資的雇員職務(wù)為CLERK,且修改的工資超過(guò)200時(shí)才進(jìn)行觸發(fā),否則不進(jìn)行觸發(fā)。?
所以在驗(yàn)證過(guò)程中,雖然修改了3條記錄,但通過(guò)查詢語(yǔ)句發(fā)現(xiàn):第一條修改語(yǔ)句修改編號(hào)為7788的SCOTT記錄,因?yàn)镾COTT的職務(wù)是ANALYST,不符合WHEN條件,沒(méi)有引起觸發(fā)器動(dòng)作;第二條修改語(yǔ)句修改編號(hào)為7369的SMITH的記錄,職務(wù)為CLERK,因?yàn)樵黾拥墓べY(500)超過(guò)了200,所以引起觸發(fā)器動(dòng)作,并在LOGERR表中進(jìn)行了記錄;第三條修改語(yǔ)句修改編號(hào)為7876的雇員ADAMS的記錄,雖然ADAMS的職務(wù)為CLERK,但修改的工資(50)沒(méi)有超過(guò)200,所以沒(méi)有引起觸發(fā)器動(dòng)作。?
注意:在WHEN條件中引用new和old不需要在前面加“: ”。?
在以上實(shí)例中,記錄了對(duì)工資的修改超出范圍的信息,但沒(méi)有限制對(duì)工資的修改。那么當(dāng)對(duì)雇員工資的修改幅度不滿足條件時(shí),能否直接限制對(duì)工資的修改呢?答案是肯定的。?
【訓(xùn)練3】? 創(chuàng)建觸發(fā)器CHECK_SAL,當(dāng)對(duì)職務(wù)為CLERK的雇員的工資修改超出500至2000的范圍時(shí),進(jìn)行限制。?
步驟1:創(chuàng)建和編譯以下觸發(fā)器:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
步驟2:在EMP表中插入記錄:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
步驟3:檢查工資的修改結(jié)果:?
Sql代碼??
執(zhí)行結(jié)果為:?
??? Sql代碼??
說(shuō)明:在觸發(fā)器中,當(dāng)IF語(yǔ)句的條件滿足時(shí),即對(duì)職務(wù)為CLERK的雇員工資的修改超出指定范圍時(shí),用RAISE_APPLICATION_ERROR語(yǔ)句來(lái)定義一個(gè)臨時(shí)定義的異常,并立即引發(fā)異常。由于觸發(fā)器是BEFORE類型,因此觸發(fā)器先執(zhí)行,觸發(fā)器因異常而終止,SQL語(yǔ)句的執(zhí)行就會(huì)取消。?
通過(guò)步驟2的執(zhí)行信息可以看到,第一條語(yǔ)句修改編號(hào)為7876的雇員ADAMS的工資為800,成功執(zhí)行。第二條語(yǔ)句修改雇員ADAMS的工資為450,發(fā)生異常,執(zhí)行失敗。這樣就阻止了不符合條件的工資的修改。通過(guò)步驟3的查詢可以看到,雇員ADAMS最后的工資是800,即發(fā)生異常之前的修改結(jié)果。?
【練習(xí)3】限定對(duì)emp表的修改,只能修改部門(mén)10的雇員工資。?
?? 【訓(xùn)練4】? 創(chuàng)建一個(gè)行級(jí)觸發(fā)器CASCADE_UPDATE,當(dāng)修改部門(mén)編號(hào)時(shí),EMP表的相關(guān)行的部門(mén)編號(hào)也自動(dòng)修改。該觸發(fā)器稱為級(jí)聯(lián)修改觸發(fā)器。?
步驟1:創(chuàng)建和編譯以下觸發(fā)器:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
步驟2:驗(yàn)證觸發(fā)器:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
執(zhí)行查詢:?
Sql代碼??
執(zhí)行結(jié)果:?
?? Sql代碼??
說(shuō)明:通過(guò)檢查雇員的部門(mén)編號(hào),發(fā)現(xiàn)原來(lái)編號(hào)為10的部門(mén)編號(hào)被修改為11。?
本例中的UPDATE OF deptno表示只有在修改表的DEPTNO列時(shí)才引發(fā)觸發(fā)器,對(duì)其他列的修改不會(huì)引起觸發(fā)器的動(dòng)作。在觸發(fā)器中,對(duì)雇員表的部門(mén)編號(hào)與修改之前的部門(mén)編號(hào)一樣的雇員,修改其部門(mén)編號(hào)為新的部門(mén)編號(hào)。注意,在語(yǔ)句中同時(shí)用到了:new和:old來(lái)引用修改部門(mén)編號(hào)前后的部門(mén)編號(hào)。?
【練習(xí)4】建立級(jí)聯(lián)刪除觸發(fā)器CASCADE_DELETE,當(dāng)刪除部門(mén)時(shí),級(jí)聯(lián)刪除EMP表的雇員記錄。?
利用觸發(fā)器還可以修改數(shù)據(jù)。?
【訓(xùn)練5】? 將插入的雇員的名字變成以大寫(xiě)字母開(kāi)頭。?
步驟1:創(chuàng)建和編譯以下觸發(fā)器:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
步驟2:驗(yàn)證運(yùn)行結(jié)果:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
執(zhí)行查詢:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
說(shuō)明:在本例中,通過(guò)直接為:new.ename進(jìn)行賦值,修改了插入的值,但是這種用法只能在BEFORE型觸發(fā)器中使用。驗(yàn)證結(jié)果為,在插入語(yǔ)句中雇員名稱為大寫(xiě)的BILL,查詢結(jié)果中雇員名稱已經(jīng)轉(zhuǎn)換成以大寫(xiě)開(kāi)頭的Bill。?
【練習(xí)5】限定一次對(duì)雇員的工資修改不超過(guò)原工資的10%。?
語(yǔ)句級(jí)觸發(fā)器的應(yīng)用 ?
同行級(jí)觸發(fā)器不同,語(yǔ)句級(jí)觸發(fā)器的每個(gè)操作語(yǔ)句不管操作的行數(shù)是多少,只觸發(fā)一次觸發(fā)器,所以語(yǔ)句級(jí)觸發(fā)器適合于對(duì)整個(gè)表的操作權(quán)限等進(jìn)行控制。在觸發(fā)器定義中若省略FOR EACH ROW子句,則為語(yǔ)句級(jí)觸發(fā)器。?
【訓(xùn)練1】? 創(chuàng)建一個(gè)語(yǔ)句級(jí)觸發(fā)器CHECK_TIME,限定對(duì)表EMP的修改時(shí)間為周一至周五的早8點(diǎn)至晚5點(diǎn)。?
步驟1:創(chuàng)建和編譯以下觸發(fā)器:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
步驟2:當(dāng)前時(shí)間為18點(diǎn)50分,在EMP表中插入記錄:?
Sql代碼??
顯示結(jié)果為:?
Sql代碼??
? 說(shuō)明:通過(guò)引發(fā)異常限制對(duì)數(shù)據(jù)庫(kù)進(jìn)行的插入、刪除和修改操作的時(shí)間。SYSDATE用來(lái)獲取系統(tǒng)當(dāng)前時(shí)間,并按不同的格式字符串進(jìn)行轉(zhuǎn)換。“DY”表示獲取英文表示的星期簡(jiǎn)寫(xiě),“HH24”表示獲取24小時(shí)制時(shí)間的小時(shí)。?
當(dāng)在18點(diǎn)50分修改表中的數(shù)據(jù)時(shí),由于時(shí)間在8點(diǎn)至17點(diǎn)(晚5點(diǎn))之外,所以產(chǎn)生“非法時(shí)間修改表錯(cuò)誤”的用戶自定義錯(cuò)誤,修改操作終止。?
【練習(xí)1】設(shè)計(jì)一個(gè)語(yǔ)句級(jí)觸發(fā)器,限定只能對(duì)數(shù)據(jù)庫(kù)進(jìn)行修改操作,不能對(duì)數(shù)據(jù)庫(kù)進(jìn)行插入和刪除操作。在需要進(jìn)行插入和刪除時(shí),將觸發(fā)器設(shè)置為無(wú)效狀態(tài),完成后重新設(shè)置為生效狀態(tài)。?
數(shù)據(jù)庫(kù)事件觸發(fā)器 ?
數(shù)據(jù)庫(kù)事件觸發(fā)器有數(shù)據(jù)庫(kù)級(jí)和模式級(jí)兩種。前者定義在整個(gè)數(shù)據(jù)庫(kù)上,觸發(fā)事件是數(shù)據(jù)庫(kù)事件,如數(shù)據(jù)庫(kù)的啟動(dòng)、關(guān)閉,對(duì)數(shù)據(jù)庫(kù)的登錄或退出。后者定義在模式上,觸發(fā)事件包括模式用戶的登錄或退出,或?qū)?shù)據(jù)庫(kù)對(duì)象的創(chuàng)建和修改(DDL事件)。?
數(shù)據(jù)庫(kù)事件觸發(fā)器的觸發(fā)事件的種類和級(jí)別如表9-3所示。?
Sql代碼??
定義數(shù)據(jù)庫(kù)事件和模式事件觸發(fā)器 ?
創(chuàng)建數(shù)據(jù)庫(kù)級(jí)觸發(fā)器需要ADMINISTER DATABASE TRIGGER系統(tǒng)權(quán)限,一般只有系統(tǒng)管理員擁有該權(quán)限。?
對(duì)于模式級(jí)觸發(fā)器,為自己的模式創(chuàng)建觸發(fā)器需要CREATE TRIGGER權(quán)限,如果是為其他模式創(chuàng)建觸發(fā)器,需要CREATE ANY TRIGGER權(quán)限。?
數(shù)據(jù)庫(kù)事件和模式事件觸發(fā)器的創(chuàng)建語(yǔ)法與DML觸發(fā)器的創(chuàng)建語(yǔ)法類似。數(shù)據(jù)庫(kù)事件或模式事件觸發(fā)器的創(chuàng)建語(yǔ)法如下:?
CREATE [OR REPLACE] TRIGGER 觸發(fā)器名?
{BEFORE|AFTER }?
{DDL事件1 [DDL事件2...]| 數(shù)據(jù)庫(kù)事件1 [數(shù)據(jù)庫(kù)事件2...]}?
ON {DATABASE| [模式名.]SCHEMA }?
[WHEN (條件)]?
DECLARE?
聲明部分?
BEGIN?
主體部分?
END;?
其中:DATABASE表示創(chuàng)建數(shù)據(jù)庫(kù)級(jí)觸發(fā)器,數(shù)據(jù)庫(kù)級(jí)要給出數(shù)據(jù)庫(kù)事件;SCHEMA表示創(chuàng)建模式級(jí)觸發(fā)器,模式級(jí)要給出模式事件或DDL事件。?
在數(shù)據(jù)庫(kù)事件觸發(fā)器中,可以使用如表9-4所示的一些事件屬性。不同類型的觸發(fā)器可以使用的事件屬性有所不同。?
Sql代碼??
數(shù)據(jù)庫(kù)事件觸發(fā)器 ?
下面是一個(gè)綜合的數(shù)據(jù)庫(kù)事件觸發(fā)器練習(xí)。先為STUDENT賬戶授予創(chuàng)建數(shù)據(jù)庫(kù)事件觸發(fā)器的權(quán)限,ADMINISTER DATABASE TRIGGER,然后創(chuàng)建有關(guān)的表和觸發(fā)器,最后予以驗(yàn)證。?
? 【訓(xùn)練1】? 創(chuàng)建觸發(fā)器,對(duì)本次數(shù)據(jù)庫(kù)啟動(dòng)以來(lái)的用戶登錄時(shí)間進(jìn)行記錄,每次數(shù)據(jù)庫(kù)啟動(dòng)后,先清空該表。?
步驟1:創(chuàng)建登錄事件記錄表:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
步驟2:創(chuàng)建數(shù)據(jù)庫(kù)STARTUP事件觸發(fā)器:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
步驟3:創(chuàng)建數(shù)據(jù)庫(kù)LOGON事件觸發(fā)器:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
步驟4:驗(yàn)證DATABASE_LOGON觸發(fā)器:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
執(zhí)行查詢:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
步驟5:驗(yàn)證INIT_LOGON觸發(fā)器。?
重新啟動(dòng)數(shù)據(jù)庫(kù),登錄STUDENT賬戶:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
說(shuō)明:本例中共創(chuàng)建了兩個(gè)數(shù)據(jù)庫(kù)級(jí)事件觸發(fā)器。DATABASE_LOGON在用戶登錄時(shí)觸發(fā),向表userlog中增加一條記錄,記錄登錄用戶名和登錄時(shí)間。INIT_LOGON在數(shù)據(jù)庫(kù)啟動(dòng)時(shí)觸發(fā),清除userlog表中記錄的數(shù)據(jù)。所以當(dāng)數(shù)據(jù)庫(kù)重新啟動(dòng)后,重新登錄STUDENT賬戶,此時(shí)userlog表中只有一條記錄。?
【訓(xùn)練2】? 創(chuàng)建STUDENT_LOGON模式級(jí)觸發(fā)器,專門(mén)記錄STUDENT賬戶的登錄時(shí)間:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
說(shuō)明:為當(dāng)前模式創(chuàng)建觸發(fā)器,可以省略SCHEMA前面的模式名。?
【練習(xí)1】修改DATABASE_LOGON觸發(fā)器和userlog表,增加對(duì)退出時(shí)間的記錄。?
DDL事件觸發(fā)器 ?
【訓(xùn)練1】? 通過(guò)觸發(fā)器阻止對(duì)emp表的刪除。?
步驟1:創(chuàng)建DDL觸發(fā)器:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
步驟2:通過(guò)刪除emp表驗(yàn)證觸發(fā)器:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
? ?? 說(shuō)明:該觸發(fā)器阻止在當(dāng)前模式下對(duì)emp表的刪除,但不阻止刪除其他對(duì)象。Sys.Dictionary_obj_name屬性返回要?jiǎng)h除的對(duì)象名稱。?
替代觸發(fā)器 ?
【訓(xùn)練1】? 在emp表的視圖上,通過(guò)觸發(fā)器修改emp表。?
步驟1:創(chuàng)建視圖emp_name:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
步驟1:創(chuàng)建替代觸發(fā)器:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
步驟2:向emp_name視圖插入記錄:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
? 說(shuō)明:向視圖直接插入雇員名將會(huì)發(fā)生錯(cuò)誤,因?yàn)閑mp表的雇員編號(hào)列不允許為空。通過(guò)創(chuàng)建替代觸發(fā)器,將向視圖插入雇員名稱轉(zhuǎn)換為向emp表插入雇員編號(hào)和雇員名稱,雇員編號(hào)取當(dāng)前的最大雇員編號(hào)加1。試檢查emp表的雇員列表。?
【訓(xùn)練2】? 在emp表的視圖emp_name上,通過(guò)觸發(fā)器阻止對(duì)emp表的刪除。?
步驟1:阻止通過(guò)視圖刪除雇員,并顯示用戶自定義錯(cuò)誤信息:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
步驟2:通過(guò)對(duì)視圖進(jìn)行刪除來(lái)驗(yàn)證觸發(fā)器:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
說(shuō)明:可以通過(guò)視圖emp_name對(duì)雇員進(jìn)行刪除,比如執(zhí)行DELETE FROM emp_name語(yǔ)句將刪除雇員表的全部雇員。但是由于在emp_name視圖中只能看到一部分雇員信息,所以刪除可能會(huì)產(chǎn)生誤操作。通過(guò)定義一個(gè)替代觸發(fā)器,可阻止通過(guò)emp_name視圖對(duì)emp表雇員進(jìn)行刪除,但不阻止直接對(duì)emp表進(jìn)行刪除。?
查看觸發(fā)器 ?
【訓(xùn)練1】? 顯示觸發(fā)器CHECK_TIME的體部分:?
Sql代碼??
結(jié)果為:?
Sql代碼??
階段訓(xùn)練 ?
【訓(xùn)練1】? 創(chuàng)建觸發(fā)器,進(jìn)行表的同步復(fù)制。?
步驟1:創(chuàng)建emp表的復(fù)本employee:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
步驟2:創(chuàng)建和編譯以下觸發(fā)器:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
步驟3:對(duì)emp表進(jìn)行插入、刪除和更新:?
Sql代碼??
執(zhí)行結(jié)果:?
Sql代碼??
步驟4:檢查emp表和employee表中被插入、刪除和更新的雇員。?
運(yùn)行結(jié)果略,請(qǐng)自行驗(yàn)證。?
? 說(shuō)明:在觸發(fā)器中判斷觸發(fā)事件,根據(jù)不同的事件對(duì)employee表進(jìn)行不同的操作。?
【練習(xí)1】創(chuàng)建一個(gè)emp表的觸發(fā)器EMP_TOTAL,每當(dāng)向雇員表插入、刪除或更新雇員信息時(shí),將新的統(tǒng)計(jì)信息存入統(tǒng)計(jì)表EMPTOTAL,使統(tǒng)計(jì)表總能夠反映最新的統(tǒng)計(jì)信息。?
統(tǒng)計(jì)表是記錄各部門(mén)雇員總?cè)藬?shù)、總工資的統(tǒng)計(jì)表,結(jié)構(gòu)如下:?
部門(mén)編號(hào) number(2)?
總?cè)藬?shù) number(5)?
總工資 number(10,2)?
練習(xí) ?
1. 下列有關(guān)觸發(fā)器和存儲(chǔ)過(guò)程的描述,正確的是:?
A. 兩者都可以傳遞參數(shù)?
B. 兩者都可以被其他程序調(diào)用?
C. 兩種模塊中都可以包含數(shù)據(jù)庫(kù)事務(wù)語(yǔ)句?
D. 兩者創(chuàng)建的系統(tǒng)權(quán)限不同?
2. 下列事件,屬于DDL事件的是:?
??? A.? INSERT B.? LOGON?
??? C.? DROP D.? SERVERERROR?
3. 假定在一個(gè)表上同時(shí)定義了行級(jí)和語(yǔ)句級(jí)觸發(fā)器,在一次觸發(fā)當(dāng)中,下列說(shuō)法正確的是:?
??? A. 語(yǔ)句級(jí)觸發(fā)器只執(zhí)行一次?
??? B. 語(yǔ)句級(jí)觸發(fā)器先于行級(jí)觸發(fā)器執(zhí)行?
?????????? C. 行級(jí)觸發(fā)器先于語(yǔ)句級(jí)觸發(fā)器執(zhí)行?
??????????????? D. 行級(jí)觸發(fā)器對(duì)表的每一行都會(huì)執(zhí)行一次?
4. 有關(guān)行級(jí)觸發(fā)器的偽記錄,下列說(shuō)法正確的是:?
?? A.? INSERT事件觸發(fā)器中,可以使用:old偽記錄。?
?? B.? DELETE事件觸發(fā)器中,可以使用:new偽記錄。?
?? C.? UPDATA事件觸發(fā)器中,只能使用:new偽記錄。?
??????????????? D.? UPDATA事件觸發(fā)器中,可以使用:old偽記錄。?
5. 下列有關(guān)替代觸發(fā)器的描述,正確的是:?
??? A. 替代觸發(fā)器創(chuàng)建在表上?
??????????????? B. 替代觸發(fā)器創(chuàng)建在數(shù)據(jù)庫(kù)上?
??????????????? C. 通過(guò)替代觸發(fā)器可以向基表插入數(shù)據(jù)?
??????????????? D. 通過(guò)替代觸發(fā)器可以向視圖插入數(shù)據(jù)?
總結(jié)
以上是生活随笔為你收集整理的oracle 触发器的种类和触发事件的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: RMAN 备份恢复
- 下一篇: undo表空间暴长,如何取消自动扩展