2009-04-14
oracle 觸發(fā)器的種類和觸發(fā)事件,DML觸發(fā)器,DDL事件觸發(fā)器,替代觸發(fā)器,查看觸發(fā)器,
關(guān)鍵字: oracle 觸發(fā)器 種類 觸發(fā) 事件 dml ddl 事件 替代 查看觸發(fā)器的種類和觸發(fā)事件
觸發(fā)器必須由事件才能觸發(fā)。觸發(fā)器的觸發(fā)事件分可為3類,分別是DML事件、DDL事件和數(shù)據(jù)庫(kù)事件。
每類事件包含若干個(gè)事件,如下所示。數(shù)據(jù)庫(kù)的事件是具體的,在創(chuàng)建觸發(fā)器時(shí)要指明觸發(fā)的事件。
種 類 關(guān) 鍵 字 含 義
觸發(fā)器的類型可劃分為4種:數(shù)據(jù)操縱語(yǔ)言(DML)觸發(fā)器、替代(INSTEAD OF)觸發(fā)器、數(shù)據(jù)定義語(yǔ)言(DDL)觸發(fā)器和數(shù)據(jù)庫(kù)事件觸發(fā)器。
各類觸發(fā)器的作用如下所示。
DML觸發(fā)器的要點(diǎn)
DML觸發(fā)器是定義在表上的觸發(fā)器,由DML事件引發(fā)。編寫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)閉或重新打開,即當(dāng)觸發(fā)器暫時(shí)不用時(shí),可以將其置成無(wú)效狀態(tài),在使用時(shí)重新打開。該命令語(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:
執(zhí)行結(jié)果:
步驟2:創(chuàng)建一個(gè)LOGS表的主鍵序列LOGS_ID_SEQ:
執(zhí)行結(jié)果:
步驟3:創(chuàng)建和編譯以下觸發(fā)器:
執(zhí)行結(jié)果:
觸發(fā)器已創(chuàng)建
步驟4:在EMP表中插入記錄:
執(zhí)行結(jié)果:
步驟5:檢查L(zhǎng)OGS表中記錄的信息:
執(zhí)行結(jié)果為:
說(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:
執(zhí)行結(jié)果:
步驟2:創(chuàng)建和編譯以下觸發(fā)器:
執(zhí)行結(jié)果:
步驟3:在EMP表中更新記錄:
執(zhí)行結(jié)果:
步驟4:檢查L(zhǎng)OGSAL表中記錄的信息:
執(zhí)行結(jié)果為:
說(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ā)器:
執(zhí)行結(jié)果:
步驟2:在EMP表中插入記錄:
執(zhí)行結(jié)果:
步驟3:檢查工資的修改結(jié)果:
執(zhí)行結(jié)果為:
說(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表的修改,只能修改部門10的雇員工資。
【訓(xùn)練4】 創(chuàng)建一個(gè)行級(jí)觸發(fā)器CASCADE_UPDATE,當(dāng)修改部門編號(hào)時(shí),EMP表的相關(guān)行的部門編號(hào)也自動(dòng)修改。該觸發(fā)器稱為級(jí)聯(lián)修改觸發(fā)器。
步驟1:創(chuàng)建和編譯以下觸發(fā)器:
執(zhí)行結(jié)果:
步驟2:驗(yàn)證觸發(fā)器:
執(zhí)行結(jié)果:
執(zhí)行查詢:
執(zhí)行結(jié)果:
說(shuō)明:通過(guò)檢查雇員的部門編號(hào),發(fā)現(xiàn)原來(lái)編號(hào)為10的部門編號(hào)被修改為11。
本例中的UPDATE OF deptno表示只有在修改表的DEPTNO列時(shí)才引發(fā)觸發(fā)器,對(duì)其他列的修改不會(huì)引起觸發(fā)器的動(dòng)作。在觸發(fā)器中,對(duì)雇員表的部門編號(hào)與修改之前的部門編號(hào)一樣的雇員,修改其部門編號(hào)為新的部門編號(hào)。注意,在語(yǔ)句中同時(shí)用到了:new和:old來(lái)引用修改部門編號(hào)前后的部門編號(hào)。
【練習(xí)4】建立級(jí)聯(lián)刪除觸發(fā)器CASCADE_DELETE,當(dāng)刪除部門時(shí),級(jí)聯(lián)刪除EMP表的雇員記錄。
利用觸發(fā)器還可以修改數(shù)據(jù)。
【訓(xùn)練5】 將插入的雇員的名字變成以大寫字母開頭。
步驟1:創(chuàng)建和編譯以下觸發(fā)器:
執(zhí)行結(jié)果:
步驟2:驗(yàn)證運(yùn)行結(jié)果:
執(zhí)行結(jié)果:
執(zhí)行查詢:
執(zhí)行結(jié)果:
說(shuō)明:在本例中,通過(guò)直接為:new.ename進(jìn)行賦值,修改了插入的值,但是這種用法只能在BEFORE型觸發(fā)器中使用。驗(yàn)證結(jié)果為,在插入語(yǔ)句中雇員名稱為大寫的BILL,查詢結(jié)果中雇員名稱已經(jīng)轉(zhuǎn)換成以大寫開頭的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ā)器:
執(zhí)行結(jié)果:
步驟2:當(dāng)前時(shí)間為18點(diǎn)50分,在EMP表中插入記錄:
顯示結(jié)果為:
說(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)寫,“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所示。
定義數(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ā)器可以使用的事件屬性有所不同。
數(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)建登錄事件記錄表:
執(zhí)行結(jié)果:
步驟2:創(chuàng)建數(shù)據(jù)庫(kù)STARTUP事件觸發(fā)器:
執(zhí)行結(jié)果:
步驟3:創(chuàng)建數(shù)據(jù)庫(kù)LOGON事件觸發(fā)器:
執(zhí)行結(jié)果:
步驟4:驗(yàn)證DATABASE_LOGON觸發(fā)器:
執(zhí)行結(jié)果:
執(zhí)行查詢:
執(zhí)行結(jié)果:
步驟5:驗(yàn)證INIT_LOGON觸發(fā)器。
重新啟動(dòng)數(shù)據(jù)庫(kù),登錄STUDENT賬戶:
執(zhí)行結(jié)果:
說(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ā)器,專門記錄STUDENT賬戶的登錄時(shí)間:
執(zhí)行結(jié)果:
說(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ā)器:
執(zhí)行結(jié)果:
步驟2:通過(guò)刪除emp表驗(yàn)證觸發(fā)器:
執(zhí)行結(jié)果:
說(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:
執(zhí)行結(jié)果:
步驟1:創(chuàng)建替代觸發(fā)器:
執(zhí)行結(jié)果:
步驟2:向emp_name視圖插入記錄:
執(zhí)行結(jié)果:
說(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ò)誤信息:
執(zhí)行結(jié)果:
步驟2:通過(guò)對(duì)視圖進(jìn)行刪除來(lái)驗(yàn)證觸發(fā)器:
執(zhí)行結(jié)果:
說(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的體部分:
結(jié)果為:
階段訓(xùn)練
【訓(xùn)練1】 創(chuàng)建觸發(fā)器,進(jìn)行表的同步復(fù)制。
步驟1:創(chuàng)建emp表的復(fù)本employee:
執(zhí)行結(jié)果:
步驟2:創(chuàng)建和編譯以下觸發(fā)器:
執(zhí)行結(jié)果:
步驟3:對(duì)emp表進(jìn)行插入、刪除和更新:
執(zhí)行結(jié)果:
步驟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ì)表是記錄各部門雇員總?cè)藬?shù)、總工資的統(tǒng)計(jì)表,結(jié)構(gòu)如下:
部門編號(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) 鍵 字 含 義
- 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ù)打開時(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ù)中斷開時(shí)觸發(fā)
- SERVERERROR 發(fā)生服務(wù)器錯(cuò)誤時(shí)觸發(fā)
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ù)打開時(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ù)中斷開時(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ā)器的作用如下所示。
- 種 類 簡(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ù)事件
種 類 簡(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ā)。編寫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)閉或重新打開,即當(dāng)觸發(fā)器暫時(shí)不用時(shí),可以將其置成無(wú)效狀態(tài),在使用時(shí)重新打開。該命令語(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:
- 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)
- );
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é)果:
- 表已創(chuàng)建。
表已創(chuàng)建。
步驟2:創(chuàng)建一個(gè)LOGS表的主鍵序列LOGS_ID_SEQ:
- CREATE SEQUENCE logs_id_squ INCREMENT BY 1
- START WITH 1 MAXVALUE 9999999 NOCYCLE NOCACHE;
CREATE SEQUENCE logs_id_squ INCREMENT BY 1START WITH 1 MAXVALUE 9999999 NOCYCLE NOCACHE;
執(zhí)行結(jié)果:
- 序列已創(chuàng)建。
序列已創(chuàng)建。
步驟3:創(chuàng)建和編譯以下觸發(fā)器:
- 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;
CREATE OR REPLACE TRIGGER DML_LOGBEFORE --觸發(fā)時(shí)間為操作前DELETE OR INSERT OR UPDATE -- 由三種事件觸發(fā)ON empFOR EACH ROW -- 行級(jí)觸發(fā)器BEGINIF INSERTING THENINSERT INTO logs VALUES(logs_id_squ.NEXTVAL,'EMP','INSERT',:new.empno,SYSDATE,USER);ELSIF DELETING THENINSERT INTO logs VALUES(logs_id_squ.NEXTVAL,'EMP','DELETE',:old.empno,SYSDATE,USER);ELSEINSERT INTO logs VALUES(logs_id_squ.NEXTVAL,'EMP','UPDATE',:new.empno,SYSDATE,USER);END IF;END;
執(zhí)行結(jié)果:
觸發(fā)器已創(chuàng)建
步驟4:在EMP表中插入記錄:
- INSERT INTO emp(empno,ename,job,sal) VALUES(8001,'MARY','CLERK',1000);
- COMMIT;
INSERT INTO emp(empno,ename,job,sal) VALUES(8001,'MARY','CLERK',1000);COMMIT;
執(zhí)行結(jié)果:
- 已創(chuàng)建1行。
- 提交完成。
已創(chuàng)建1行。提交完成。
步驟5:檢查L(zhǎng)OGS表中記錄的信息:
- SELECT * FROM LOGS;
SELECT * FROM LOGS;
執(zhí)行結(jié)果為:
- LOG_ID LOG_TABLE LOG_DML LOG_KEY_ID LOG_DATE LOG_USER
- ----------------- ----------------- ------------------ ----------------------- ---------------- -------------------
- 1 EMP INSERT 8001 29-3月 -04 STUDENT
- 已選擇 1 行。
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:
- CREATE TABLE logerr(
- NUM NUMBER(10) NOT NULL,
- MESSAGE VARCHAR2(50) NOT NULL
- );
CREATE TABLE logerr(NUM NUMBER(10) NOT NULL,MESSAGE VARCHAR2(50) NOT NULL);
執(zhí)行結(jié)果:
- 表已創(chuàng)建。
表已創(chuàng)建。
步驟2:創(chuàng)建和編譯以下觸發(fā)器:
- 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;
CREATE OR REPLACE TRIGGER log_salBEFOREUPDATE OF salON empFOR EACH ROWWHEN (new.job='CLERK' AND (ABS(new.sal-old.sal)>200))DECLAREv_no NUMBER;BEGINSELECT COUNT(*) INTO v_no FROM logerr;INSERT INTO logerr VALUES(v_no+1,'雇員'||:new.ename||'的原工資:'||:old.sal||'新工資:'||:new.sal);END;
執(zhí)行結(jié)果:
- 觸發(fā)器已創(chuàng)建。
觸發(fā)器已創(chuàng)建。
步驟3:在EMP表中更新記錄:
- 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;
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é)果:
- 已更新 1 行。
- 已更新 1 行。
- 已更新 1 行。
- 提交完成。
已更新 1 行。已更新 1 行。已更新 1 行。提交完成。
步驟4:檢查L(zhǎng)OGSAL表中記錄的信息:
- SELECT * FROM logerr;
SELECT * FROM logerr;
執(zhí)行結(jié)果為:
- NUM MESSAGE
- ------------------ --------------------------------------------------------
- 1 雇員SMITH的原工資:800新工資:1300
- 已選擇 1 行。
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ā)器:
- 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;
CREATE OR REPLACE TRIGGER CHECK_SALBEFOREUPDATEON empFOR EACH ROWBEGINIF :new.job='CLERK' AND (:new.sal<500 OR :new.sal>2000) THENRAISE_APPLICATION_ERROR(-20001, '工資修改超出范圍,操作取消!');END IF;END;
執(zhí)行結(jié)果:
- 觸發(fā)器已創(chuàng)建。
觸發(fā)器已創(chuàng)建。
步驟2:在EMP表中插入記錄:
- UPDATE emp SET sal=800 WHERE empno=7876;
- UPDATE emp SET sal=450 WHERE empno=7876;
- COMMIT;
UPDATE emp SET sal=800 WHERE empno=7876;UPDATE emp SET sal=450 WHERE empno=7876;COMMIT;
執(zhí)行結(jié)果:
- 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ò)提交完成。
UPDATE emp SET sal=450 WHERE empno=7876*ERROR 位于第 1 行:ORA-20001: 工資修改超出范圍,操作取消!ORA-06512: 在"STUDENT.CHECK_SAL", line 3ORA-04088: 觸發(fā)器 'STUDENT.CHECK_SAL' 執(zhí)行過(guò)程中出錯(cuò)提交完成。
步驟3:檢查工資的修改結(jié)果:
- SELECT empno,ename,job,sal FROM emp WHERE empno=7876;
SELECT empno,ename,job,sal FROM emp WHERE empno=7876;
執(zhí)行結(jié)果為:
- EMPNO ENAME JOB SAL
- ----------------- ------------- ------------- ------------------------
- 7876 ADAMS CLERK 800
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表的修改,只能修改部門10的雇員工資。
【訓(xùn)練4】 創(chuàng)建一個(gè)行級(jí)觸發(fā)器CASCADE_UPDATE,當(dāng)修改部門編號(hào)時(shí),EMP表的相關(guān)行的部門編號(hào)也自動(dòng)修改。該觸發(fā)器稱為級(jí)聯(lián)修改觸發(fā)器。
步驟1:創(chuàng)建和編譯以下觸發(fā)器:
- 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;
CREATE TRIGGER CASCADE_UPDATEAFTERUPDATE OF deptnoON DEPTFOR EACH ROWBEGINUPDATEEMP SET EMP.DEPTNO=:NEW.DEPTNOWHERE EMP.DEPTNO=:OLD.DEPTNO;END;
執(zhí)行結(jié)果:
- 觸發(fā)器已創(chuàng)建
觸發(fā)器已創(chuàng)建
步驟2:驗(yàn)證觸發(fā)器:
- UPDATE dept SET deptno=11 WHERE deptno=10;
- COMMIT;
UPDATE dept SET deptno=11 WHERE deptno=10;COMMIT;
執(zhí)行結(jié)果:
- 已更新 1 行。
已更新 1 行。
執(zhí)行查詢:
- SELECT empno,ename,deptno FROM emp;
SELECT empno,ename,deptno FROM emp;
執(zhí)行結(jié)果:
- 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
EMPNO ENAME DEPTNO----------------- ----------- -------------------------7369 SMITH 207499 ALLEN 307521 WARD 307566 JONES 207654 MARTIN 307698 BLAKE 307782 CLARK 117839 KING 117844 TURNER 307876 ADAMS 207900 JAMES 307902 FORD 207934 MILLER 117788 SCOTT 20
說(shuō)明:通過(guò)檢查雇員的部門編號(hào),發(fā)現(xiàn)原來(lái)編號(hào)為10的部門編號(hào)被修改為11。
本例中的UPDATE OF deptno表示只有在修改表的DEPTNO列時(shí)才引發(fā)觸發(fā)器,對(duì)其他列的修改不會(huì)引起觸發(fā)器的動(dòng)作。在觸發(fā)器中,對(duì)雇員表的部門編號(hào)與修改之前的部門編號(hào)一樣的雇員,修改其部門編號(hào)為新的部門編號(hào)。注意,在語(yǔ)句中同時(shí)用到了:new和:old來(lái)引用修改部門編號(hào)前后的部門編號(hào)。
【練習(xí)4】建立級(jí)聯(lián)刪除觸發(fā)器CASCADE_DELETE,當(dāng)刪除部門時(shí),級(jí)聯(lián)刪除EMP表的雇員記錄。
利用觸發(fā)器還可以修改數(shù)據(jù)。
【訓(xùn)練5】 將插入的雇員的名字變成以大寫字母開頭。
步驟1:創(chuàng)建和編譯以下觸發(fā)器:
- CREATE OR REPLACE TRIGGER INITCAP
- BEFORE INSERT
- ON EMP
- FOR EACH ROW
- BEGIN
- :new.ename:=INITCAP(:new.ename);
- END;
CREATE OR REPLACE TRIGGER INITCAPBEFORE INSERTON EMPFOR EACH ROWBEGIN:new.ename:=INITCAP(:new.ename);END;
執(zhí)行結(jié)果:
- 觸發(fā)器已創(chuàng)建。
觸發(fā)器已創(chuàng)建。
步驟2:驗(yàn)證運(yùn)行結(jié)果:
- INSERT INTO emp(empno,ename,job,sal) VALUES(1000,'BILL','CLERK',1500);
INSERT INTO emp(empno,ename,job,sal) VALUES(1000,'BILL','CLERK',1500);
執(zhí)行結(jié)果:
- 已創(chuàng)建 1 行。
已創(chuàng)建 1 行。
執(zhí)行查詢:
- SELECT ename,job,sal FROM emp WHERE empno=1000;
SELECT ename,job,sal FROM emp WHERE empno=1000;
執(zhí)行結(jié)果:
- ENAME JOB SAL
- ------------- ------------- ------------------------
- Bill CLERK 1500
ENAME JOB SAL------------- ------------- ------------------------Bill CLERK 1500
說(shuō)明:在本例中,通過(guò)直接為:new.ename進(jìn)行賦值,修改了插入的值,但是這種用法只能在BEFORE型觸發(fā)器中使用。驗(yàn)證結(jié)果為,在插入語(yǔ)句中雇員名稱為大寫的BILL,查詢結(jié)果中雇員名稱已經(jīng)轉(zhuǎn)換成以大寫開頭的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ā)器:
- 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;
CREATE OR REPLACE TRIGGER CHECK_TIMEBEFOREUPDATE OR INSERT OR DELETEON EMPBEGINIF (TO_CHAR(SYSDATE,'DY') IN ('SAT','SUN'))OR TO_CHAR(SYSDATE,'HH24')< '08'OR TO_CHAR(SYSDATE,'HH24')>='17' THENRAISE_APPLICATION_ERROR(-20500,'非法時(shí)間修改表錯(cuò)誤!');END IF;END;
執(zhí)行結(jié)果:
- 觸發(fā)器已創(chuàng)建。
觸發(fā)器已創(chuàng)建。
步驟2:當(dāng)前時(shí)間為18點(diǎn)50分,在EMP表中插入記錄:
- UPDATE EMP SET SAL=3000 WHERE EMPNO=7369;
UPDATE EMP SET SAL=3000 WHERE EMPNO=7369;
顯示結(jié)果為:
- 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ò)
UPDATE EMP SET SAL=3000 WHERE EMPNO=7369*ERROR 位于第 1 行:ORA-20500: 非法時(shí)間修改表錯(cuò)誤!ORA-06512: 在"STUDENT.CHECK_TIME", line 5ORA-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)寫,“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所示。
- 種 類 關(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ù)打開時(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ù)中斷開時(shí)觸發(fā)
種 類 關(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ù)打開時(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ù)中斷開時(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ā)器可以使用的事件屬性有所不同。
- 屬 性 適用觸發(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í),返回加密后的用戶密碼
屬 性 適用觸發(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)建登錄事件記錄表:
- CREATE TABLE userlog (
- USERNAME VARCHAR2(20),
- LOGON_TIME DATE);
CREATE TABLE userlog (USERNAME VARCHAR2(20),LOGON_TIME DATE);
執(zhí)行結(jié)果:
- 表已創(chuàng)建。
表已創(chuàng)建。
步驟2:創(chuàng)建數(shù)據(jù)庫(kù)STARTUP事件觸發(fā)器:
- CREATE OR REPLACE TRIGGER INIT_LOGON
- AFTER
- STARTUP
- ON DATABASE
- BEGIN
- DELETE FROM userlog;
- END;
CREATE OR REPLACE TRIGGER INIT_LOGONAFTERSTARTUPON DATABASEBEGINDELETE FROM userlog;END;
執(zhí)行結(jié)果:
- 觸發(fā)器已創(chuàng)建。
觸發(fā)器已創(chuàng)建。
步驟3:創(chuàng)建數(shù)據(jù)庫(kù)LOGON事件觸發(fā)器:
- CREATE OR REPLACE TRIGGER DATABASE_LOGON
- AFTER
- LOGON
- ON DATABASE
- BEGIN
- INSERT INTO userlog
- VALUES(sys.login_user,sysdate);
- END;
CREATE OR REPLACE TRIGGER DATABASE_LOGONAFTERLOGONON DATABASEBEGININSERT INTO userlogVALUES(sys.login_user,sysdate);END;
執(zhí)行結(jié)果:
- 觸發(fā)器已創(chuàng)建。
觸發(fā)器已創(chuàng)建。
步驟4:驗(yàn)證DATABASE_LOGON觸發(fā)器:
- CONNECT SCOTT/TIGER@MYDB;
- CONNECT STUDENT/STUDENT@MYDB;
CONNECT SCOTT/TIGER@MYDB;CONNECT STUDENT/STUDENT@MYDB;
執(zhí)行結(jié)果:
- 已連接。
- 已連接。
已連接。已連接。
執(zhí)行查詢:
- SELECT username,TO_CHAR(logon_time,'YYYY/MM/DD HH24:MI:SS') FROM userlog;
SELECT username,TO_CHAR(logon_time,'YYYY/MM/DD HH24:MI:SS') FROM userlog;
執(zhí)行結(jié)果:
- USERNAME TO_CHAR(LOGON_TIME,
- ----------------------------- -----------------------------------------
- SCOTT 2004/03/29 22:42:20
- STUDENT 2004/03/29 22:42:20
USERNAME TO_CHAR(LOGON_TIME,----------------------------- -----------------------------------------SCOTT 2004/03/29 22:42:20STUDENT 2004/03/29 22:42:20
步驟5:驗(yàn)證INIT_LOGON觸發(fā)器。
重新啟動(dòng)數(shù)據(jù)庫(kù),登錄STUDENT賬戶:
- SELECT username,TO_CHAR(logon_time,'YYYY/MM/DD HH24:MI:SS') FROM userlog;
SELECT username,TO_CHAR(logon_time,'YYYY/MM/DD HH24:MI:SS') FROM userlog;
執(zhí)行結(jié)果:
- USERNAME TO_CHAR(LOGON_TIME,
- -------------------------------- ---------------------------------------
- STUDENT 2004/03/29 22:43:59
- 已選擇 1 行
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ā)器,專門記錄STUDENT賬戶的登錄時(shí)間:
- CREATE OR REPLACE TRIGGER STUDENT_LOGON
- AFTER
- LOGON ON STUDENT.SCHEMA
- BEGIN
- INSERT INTO userlog
- VALUES(sys.login_user,sysdate);
- END;
CREATE OR REPLACE TRIGGER STUDENT_LOGONAFTERLOGON ON STUDENT.SCHEMABEGININSERT INTO userlogVALUES(sys.login_user,sysdate);END;
執(zhí)行結(jié)果:
- 觸發(fā)器已創(chuàng)建。
觸發(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ā)器:
- 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;
CREATE OR REPLACE TRIGGER NODROP_EMPBEFOREDROP ON SCHEMABEGINIF Sys.Dictionary_obj_name='EMP' THENRAISE_APPLICATION_ERROR(-20005,'錯(cuò)誤信息:不能刪除emp表!');END IF;END;
執(zhí)行結(jié)果:
- 觸發(fā)器已創(chuàng)建。
觸發(fā)器已創(chuàng)建。
步驟2:通過(guò)刪除emp表驗(yàn)證觸發(fā)器:
- DROP TABLE emp;
DROP TABLE emp;
執(zhí)行結(jié)果:
- DROP TABLE emp
- *
- ERROR 位于第 1 行:
- ORA-00604: 遞歸 SQL 層 1 出現(xiàn)錯(cuò)誤
- ORA-20005: 錯(cuò)誤信息:不能刪除emp表!
- ORA-06512: 在line 3
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:
- CREATE VIEW emp_name AS SELECT ename FROM emp;
CREATE VIEW emp_name AS SELECT ename FROM emp;
執(zhí)行結(jié)果:
- 視圖已建立。
視圖已建立。
步驟1:創(chuàng)建替代觸發(fā)器:
- 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;
CREATE OR REPLACE TRIGGER change_nameINSTEAD OF INSERT ON emp_nameDECLAREV_EMPNO NUMBER(4);BEGINSELECT MAX(EMPNO)+1 INTO V_EMPNO FROM EMP;INSERT INTO emp(empno,ename)VALUES(V_EMPNO,:new.ename);END;
執(zhí)行結(jié)果:
- 觸發(fā)器已創(chuàng)建。
觸發(fā)器已創(chuàng)建。
步驟2:向emp_name視圖插入記錄:
- INSERT INTO emp_name VALUES('BROWN');
- COMMIT;
INSERT INTO emp_name VALUES('BROWN');COMMIT;
執(zhí)行結(jié)果:
- 已創(chuàng)建 1 行。
- 提交完成。
已創(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ò)誤信息:
- CREATE OR REPLACE TRIGGER delete_from_ename
- INSTEAD OF DELETE ON emp_name
- BEGIN
- RAISE_APPLICATION_ERROR(-20006,'錯(cuò)誤信息:不能在視圖中刪除emp表的雇員!');
- END;
CREATE OR REPLACE TRIGGER delete_from_enameINSTEAD OF DELETE ON emp_nameBEGINRAISE_APPLICATION_ERROR(-20006,'錯(cuò)誤信息:不能在視圖中刪除emp表的雇員!');END;
執(zhí)行結(jié)果:
- 觸發(fā)器已創(chuàng)建。
觸發(fā)器已創(chuàng)建。
步驟2:通過(guò)對(duì)視圖進(jìn)行刪除來(lái)驗(yàn)證觸發(fā)器:
- DELETE FROM emp_name;
DELETE FROM emp_name;
執(zhí)行結(jié)果:
- 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ò)
DELETE FROM emp_name*ERROR 位于第 1 行:ORA-20006: 錯(cuò)誤信息:不能在視圖中刪除emp表的雇員!ORA-06512:在"STUDENT.DELETE_FROM_ENAME", line 2ORA-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的體部分:
- SELECT TRIGGER_BODY FROM USER_TRIGGERS WHERE TRIGGER_NAME='CHECK_TIME';
SELECT TRIGGER_BODY FROM USER_TRIGGERS WHERE TRIGGER_NAME='CHECK_TIME';
結(jié)果為:
- TRIGGER_BODY
- ----------------------------------------------------------------------------------------
- BEGIN
- IF (TO_CHAR(SYSDATE,'DY') IN ('SAT','SUN'))
- OR TO_CHAR(SYSDATE,'HH24')<
- TRIGGER_BODY字段為L(zhǎng)ONG類型,只顯示出腳本的一部分內(nèi)容。
TRIGGER_BODY----------------------------------------------------------------------------------------BEGINIF (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:
- CREATE TABLE employee AS SELECT * FROM emp;
CREATE TABLE employee AS SELECT * FROM emp;
執(zhí)行結(jié)果:
- 表已創(chuàng)建。
表已創(chuàng)建。
步驟2:創(chuàng)建和編譯以下觸發(fā)器:
- 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;
CREATE OR REPLACE TRIGGER DUPLICATE_EMPAFTERUPDATE OR INSERT OR DELETEON EMPFOR EACH ROWBEGINIF INSERTING THENINSERT INTO employeeVALUES(:new.empno,:new.ename,:new.job,:new.mgr,:new.hiredate,:new.sal,:new.comm,:new.deptno);ELSIF DELETING THENDELETE FROM employeeWHERE empno=:old.empno;ELSEUPDATE employee SETempno=:new.empno,ename=:new.ename,job=:new.job,mgr=:new.mgr,hiredate=:new.hiredate,sal=:new.sal,comm=:new.comm,deptno=:new.deptnoWHERE empno=:old.empno;END IF;END;
執(zhí)行結(jié)果:
- 觸發(fā)器已創(chuàng)建。
觸發(fā)器已創(chuàng)建。
步驟3:對(duì)emp表進(jìn)行插入、刪除和更新:
- 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;
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é)果:
- 已刪除 1 行。
- 已創(chuàng)建 1 行。
- 已更新 1 行。
- 提交完成。
已刪除 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ì)表是記錄各部門雇員總?cè)藬?shù)、總工資的統(tǒng)計(jì)表,結(jié)構(gòu)如下:
部門編號(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ù)