CREATE SEQUENCE update_sequence INCREMENT BY 1 START WITH 1000 MAXVALUE 5000 CYCLE; 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=25 WHERE 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 >0 THEN RAISE not_on_holidays; END IF; /* check for work hours(8:00 AM to 18:00 PM */ IF (TO_CHAR(SYSDATE,'HH24')<8 OR 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;
8.1? 觸發(fā)器類型
??? 觸發(fā)器在數(shù)據(jù)庫里以獨(dú)立的對(duì)象存儲(chǔ),它與存儲(chǔ)過程和函數(shù)不同的是,存儲(chǔ)過程與函數(shù)需要用戶顯示調(diào)用才執(zhí)行,而觸發(fā)器是由一個(gè)事件來啟動(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ù)庫的表進(jìn)行的INSERT、UPDATE及DELETE操作或?qū)σ晥D進(jìn)行類似的操作。ORACLE將觸發(fā)器的功能擴(kuò)展到了觸發(fā)ORACLE,如數(shù)據(jù)庫的啟動(dòng)與關(guān)閉等。所以觸發(fā)器常用來完成由數(shù)據(jù)庫的完整性約束難以完成的復(fù)雜業(yè)務(wù)規(guī)則的約束,或用來監(jiān)視對(duì)數(shù)據(jù)庫的各種操作,實(shí)現(xiàn)審計(jì)的功能。
?
8.1.1 DML 觸發(fā)器
??? ORACLE可以在DML語句進(jìn)行觸發(fā),可以在DML操作前或操作后進(jìn)行觸發(fā),并且可以對(duì)每個(gè)行或語句操作上進(jìn)行觸發(fā)。
?
8.1.2 替代觸發(fā)器
??? 由于在ORACLE里,不能直接對(duì)由兩個(gè)以上的表建立的視圖進(jìn)行操作。所以給出了替代觸發(fā)器。它就是ORACLE 8專門為進(jìn)行視圖操作的一種處理方法。
?
8.1.3 系統(tǒng)觸發(fā)器
ORACLE 8i 提供了第三種類型的觸發(fā)器叫系統(tǒng)觸發(fā)器。它可以在ORACLE數(shù)據(jù)庫系統(tǒng)的事件中進(jìn)行觸發(fā),如ORACLE系統(tǒng)的啟動(dòng)與關(guān)閉等。
?
觸發(fā)器組成:?
l?????????觸發(fā)事件:引起觸發(fā)器被觸發(fā)的事件。 例如:DML語句(INSERT, UPDATE, DELETE語句對(duì)表或視圖執(zhí)行數(shù)據(jù)處理操作)、DDL語句(如CREATE、ALTER、DROP語句在數(shù)據(jù)庫中創(chuàng)建、修改、刪除模式對(duì)象)、數(shù)據(jù)庫系統(tǒng)事件(如系統(tǒng)啟動(dòng)或退出、異常錯(cuò)誤)、用戶事件(如登錄或退出數(shù)據(jù)庫)。
l?????????觸發(fā)時(shí)間:即該TRIGGER 是在觸發(fā)事件發(fā)生之前(BEFORE)還是之后(AFTER)觸發(fā),也就是觸發(fā)事件和該TRIGGER 的操作順序。
l?????????觸發(fā)操作:即該TRIGGER 被觸發(fā)之后的目的和意圖,正是觸發(fā)器本身要做的事情。 例如:PL/SQL 塊。
l?????????觸發(fā)對(duì)象:包括表、視圖、模式、數(shù)據(jù)庫。只有在這些對(duì)象上發(fā)生了符合觸發(fā)條件的觸發(fā)事件,才會(huì)執(zhí)行觸發(fā)操作。
l?????????觸發(fā)條件:由WHEN子句指定一個(gè)邏輯表達(dá)式。只有當(dāng)該表達(dá)式的值為TRUE時(shí),遇到觸發(fā)事件才會(huì)自動(dòng)執(zhí)行觸發(fā)器,使其執(zhí)行觸發(fā)操作。
l?????????觸發(fā)頻率:說明觸發(fā)器內(nèi)定義的動(dòng)作被執(zhí)行的次數(shù)。即語句級(jí)(STATEMENT)觸發(fā)器和行級(jí)(ROW)觸發(fā)器。
語句級(jí)(STATEMENT)觸發(fā)器:是指當(dāng)某觸發(fā)事件發(fā)生時(shí),該觸發(fā)器只執(zhí)行一次;
行級(jí)(ROW)觸發(fā)器:是指當(dāng)某觸發(fā)事件發(fā)生時(shí),對(duì)受到該操作影響的每一行數(shù)據(jù),觸發(fā)器都單獨(dú)執(zhí)行一次。
編寫觸發(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í)需要,可以先建立過程,然后在觸發(fā)器中用CALL語句進(jìn)行調(diào)用。
l????????? 在觸發(fā)器的執(zhí)行部分只能用 DML 語句( SELECT 、 INSERT 、 UPDATE 、 DELETE ),不能使用 DDL 語句( CREATE 、 ALTER 、 DROP ) 。
l???????? 觸發(fā)器中不能包含事務(wù)控制語句(COMMIT,ROLLBACK,SAVEPOINT)。因?yàn)橛|發(fā)器是觸發(fā)語句的一部分,觸發(fā)語句被提交、回退時(shí),觸發(fā)器也被提交、回退了。
l???????? 在觸發(fā)器主體中調(diào)用的任何過程、函數(shù),都不能使用事務(wù)控制語句。
l???????? 在觸發(fā)器主體中不能申明任何Long和blob變量。新值new和舊值old也不能是表中的任何long和blob列。
l???????? 不同類型的觸發(fā)器(如DML觸發(fā)器、INSTEAD OF觸發(fā)器、系統(tǒng)觸發(fā)器)的語法格式和作用有較大區(qū)別。
?
8.2? 創(chuàng)建觸發(fā)器
創(chuàng)建觸發(fā)器的一般語法是 :
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)說明觸發(fā)器為行觸發(fā)器。行觸發(fā)器和語句觸發(fā)器的區(qū)別表現(xiàn)在:行觸發(fā)器要求當(dāng)一個(gè)DML語句操作影響數(shù)據(jù)庫中的多行數(shù)據(jù)時(shí),對(duì)于其中的每個(gè)數(shù)據(jù)行,只要它們符合觸發(fā)約束條件,均激活一次觸發(fā)器;而語句觸發(fā)器將整個(gè)語句操作作為觸發(fā)事件,當(dāng)它符合約束條件時(shí),激活一次觸發(fā)器。當(dāng)省略FOR EACH ROW 選項(xiàng)時(shí),BEFORE 和AFTER 觸發(fā)器為語句觸發(fā)器,而INSTEAD OF?觸發(fā)器則只能為行觸發(fā)器。
???????????REFERENCING 子句說明相關(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 子句說明觸發(fā)約束條件。Condition 為一個(gè)邏輯表達(dá)時(shí),其中必須包含相關(guān)名稱,而不能包含查詢語句,也不能調(diào)用PL/SQL 函數(shù)。WHEN 子句指定的觸發(fā)約束條件只能用在BEFORE 和AFTER 行觸發(fā)器中,不能用在INSTEAD OF 行觸發(fā)器和其它類型的觸發(fā)器中。
??? 當(dāng)一個(gè)基表被修改( INSERT, UPDATE, DELETE)時(shí)要執(zhí)行的存儲(chǔ)過程,執(zhí)行時(shí)根據(jù)其所依附的基表改動(dòng)而自動(dòng)觸發(fā),因此與應(yīng)用程序無關(guān),用數(shù)據(jù)庫觸發(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語句級(jí)觸發(fā)器;
2.???????對(duì)與受語句影響的每一行:
l???????? 執(zhí)行 BEFORE行級(jí)觸發(fā)器
l???????? 執(zhí)行 DML語句
l???????? 執(zhí)行 AFTER行級(jí)觸發(fā)器?
3.???????執(zhí)行 AFTER語句級(jí)觸發(fā)器
?
8.2.2 創(chuàng)建 DML 觸發(fā)器
??? 觸發(fā)器名與過程名和包的名字不一樣,它是單獨(dú)的名字空間,因而觸發(fā)器名可以和表或過程有相同的名字,但在一個(gè)模式中觸發(fā)器名不能相同。
?
DML 觸發(fā)器的限制
l???????? CREATE TRIGGER語句文本的字符長(zhǎng)度不能超過32KB;
l???????? 觸發(fā)器體內(nèi)的SELECT 語句只能為SELECT … INTO …結(jié)構(gòu),或者為定義游標(biāo)所使用的SELECT 語句。
l???????? 觸發(fā)器中不能使用數(shù)據(jù)庫事務(wù)控制語句 COMMIT; ROLLBACK, SVAEPOINT 語句;
l???????? 由觸發(fā)器所調(diào)用的過程或函數(shù)也不能使用數(shù)據(jù)庫事務(wù)控制語句;
l???????? 觸發(fā)器中不能使用LONG, LONG RAW 類型;
l???????? 觸發(fā)器內(nèi)可以參照LOB 類型列的列值,但不能通過 :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í),取值為TRUE,否則為FALSE。
2)。 UPDATING [ ( column_1,column_2,…,column_x ) ] : 當(dāng)觸發(fā)事件是UPDATE????? 時(shí),如果修改了column_x列,則取值為TRUE,否則為FALSE。其中column_x是可選的。
3)。 DELETING : 當(dāng)觸發(fā)事件是DELETE時(shí),則取值為TRUE,否則為FALSE。
解發(fā)對(duì)象: 指定觸發(fā)器是創(chuàng)建在哪個(gè)表、視圖上。
l????????? 觸發(fā)類型: 是語句級(jí)還是行級(jí)觸發(fā)器。
l????????? 觸發(fā)條件: 由WHEN子句指定一個(gè)邏輯表達(dá)式, 只允許在行級(jí)觸發(fā)器上指定觸發(fā)條件,指定 UPDATING 后面的列的列表 。
?
問題:當(dāng)觸發(fā)器被觸發(fā)時(shí),要使用被插入、更新或刪除的記錄中的列值,有時(shí)要使用操作前、??????? 后列的值.
實(shí)現(xiàn):??:NEW?修飾符訪問操作完成后列的值
???????:OLD?修飾符訪問操作完成前列的值
?
特性 |
INSERT |
UPDATE |
DELETE |
OLD |
NULL |
實(shí)際值 |
實(shí)際值 |
NEW |
實(shí)際值 |
實(shí)際值 |
NULL |
?
例 1: ?建立一個(gè)觸發(fā)器, 當(dāng)職工表 emp 表被刪除一條記錄時(shí),把被刪除記錄寫到職工表刪除日志表中去。
?
CREATE?TABLE?emp_his?AS?SELECT?*?FROM?EMP?WHERE?
1
=
2
;?
CREATE?OR?REPLACE?TRIGGER?tr_del_emp?
???BEFORE?DELETE?--指定觸發(fā)時(shí)機(jī)為刪除操作前觸發(fā)
???ON?scott.emp?
???FOR?EACH?ROW???--說明創(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ì)部門號(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, '部門80的人員的工資不能降'); END IF; WHEN UPDATING ('commission_pct') THEN IF :NEW.commission_pct < :old.commission_pct THEN RAISE_APPLICATION_ERROR(-20002, '部門80的人員的獎(jiǎng)金不能降'); END IF; WHEN DELETING THEN RAISE_APPLICATION_ERROR(-20003, '不能刪除部門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表中原來在該地區(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)用過程。
?
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ǔ)過程... 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ā)器的一般語法是 :
?
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ā),所以沒有必要指定 [WHEN condition] PL/SQL_block | CALL procedure_name;
?
其中:
???????????INSTEAD OF 選項(xiàng)使ORACLE激活觸發(fā)器,而不執(zhí)行觸發(fā)事件。 只能對(duì)視圖和對(duì)象視圖建立 INSTEAD OF 觸發(fā)器,而不能對(duì)表、模式和數(shù)據(jù)庫建立 INSTEAD OF 觸發(fā)器。
???????????FOR EACH ROW選項(xiàng)說明觸發(fā)器為行觸發(fā)器。行觸發(fā)器和語句觸發(fā)器的區(qū)別表現(xiàn)在:行觸發(fā)器要求當(dāng)一個(gè)DML語句操走影響數(shù)據(jù)庫中的多行數(shù)據(jù)時(shí),對(duì)于其中的每個(gè)數(shù)據(jù)行,只要它們符合觸發(fā)約束條件,均激活一次觸發(fā)器;而語句觸發(fā)器將整個(gè)語句操作作為觸發(fā)事件,當(dāng)它符合約束條件時(shí),激活一次觸發(fā)器。當(dāng)省略FOR EACH ROW 選項(xiàng)時(shí),BEFORE 和AFTER 觸發(fā)器為語句觸發(fā)器,而INSTEAD OF 觸發(fā)器則為行觸發(fā)器。
???????????REFERENCING 子句說明相關(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 子句說明觸發(fā)約束條件。Condition 為一個(gè)邏輯表達(dá)時(shí),其中必須包含相關(guān)名稱,而不能包含查詢語句,也不能調(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ā)器來為 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)建在視圖上,并且該視圖沒有指定WITH CHECK OPTION選項(xiàng)。
l???????? 不能指定BEFORE 或 AFTER選項(xiàng)。
l???????? FOR EACH ROW子可是可選的,即INSTEAD OF觸發(fā)器只能在行級(jí)上觸發(fā)、或只能是行級(jí)觸發(fā)器,沒有必要指定。
l???????? 沒有必要在針對(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ù)庫系統(tǒng)上被觸發(fā)。DDL指的是數(shù)據(jù)定義語言,如CREATE 、ALTER及DROP 等。而數(shù)據(jù)庫系統(tǒng)事件包括數(shù)據(jù)庫服務(wù)器的啟動(dòng)或關(guān)閉,用戶的登錄與退出、數(shù)據(jù)庫服務(wù)錯(cuò)誤等。創(chuàng)建系統(tǒng)觸發(fā)器的語法如下:?
創(chuàng)建觸發(fā)器的一般語法是 :
?
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 分開;
????????database_event_list:一個(gè)或多個(gè)數(shù)據(jù)庫事件,事件間用 OR 分開;
?
???????????系統(tǒng)事件觸發(fā)器既可以建立在一個(gè)模式上,又可以建立在整個(gè)數(shù)據(jù)庫上。當(dāng)建立在模式(SCHEMA)之上時(shí),只有模式所指定用戶的DDL操作和它們所導(dǎo)致的錯(cuò)誤才激活觸發(fā)器, 默認(rèn)時(shí)為當(dāng)前用戶模式。當(dāng)建立在數(shù)據(jù)庫(DATABASE)之上時(shí),該數(shù)據(jù)庫所有用戶的DDL操作和他們所導(dǎo)致的錯(cuò)誤,以及數(shù)據(jù)庫的啟動(dòng)和關(guān)閉均可激活觸發(fā)器。要在數(shù)據(jù)庫之上建立觸發(fā)器時(shí),要求用戶具有ADMINISTER DATABASE TRIGGER權(quán)限。
?
下面給出系統(tǒng)觸發(fā)器的種類和事件出現(xiàn)的時(shí)機(jī)(前或后):
事件 |
允許的時(shí)機(jī) |
說明 |
STARTUP |
AFTER |
啟動(dòng)數(shù)據(jù)庫實(shí)例之后觸發(fā) |
SHUTDOWN |
BEFORE |
關(guān)閉數(shù)據(jù)庫實(shí)例之前觸發(fā)(非正常關(guān)閉不觸發(fā)) |
SERVERERROR |
AFTER |
數(shù)據(jù)庫服務(wù)器發(fā)生錯(cuò)誤之后觸發(fā) |
LOGON |
AFTER |
成功登錄連接到數(shù)據(jù)庫后觸發(fā) |
LOGOFF |
BEFORE |
開始斷開數(shù)據(jù)庫連接之前觸發(fā) |
CREATE |
BEFORE,AFTER |
在執(zhí)行CREATE語句創(chuàng)建數(shù)據(jù)庫對(duì)象之前、之后觸發(fā) |
DROP |
BEFORE,AFTER |
在執(zhí)行DROP語句刪除數(shù)據(jù)庫對(duì)象之前、之后觸發(fā) |
ALTER |
BEFORE,AFTER |
在執(zhí)行ALTER語句更新數(shù)據(jù)庫對(duì)象之前、之后觸發(fā) |
DDL |
BEFORE,AFTER |
在執(zhí)行大多數(shù)DDL語句之前、之后觸發(fā) |
GRANT |
BEFORE,AFTER |
執(zhí)行GRANT語句授予權(quán)限之前、之后觸發(fā) |
REVOKE |
BEFORE,AFTER |
執(zhí)行REVOKE語句收權(quán)限之前、之后觸犯發(fā) |
RENAME |
BEFORE,AFTER |
執(zhí)行RENAME語句更改數(shù)據(jù)庫對(duì)象名稱之前、之后觸犯發(fā) |
AUDIT? /? NOAUDIT |
BEFORE,AFTER |
執(zhí)行AUDIT 或 NOAUDIT進(jìn)行審計(jì)或停止審計(jì)之前、之后觸發(fā) |
?
?
8.2.4 系統(tǒng)觸發(fā)器事件屬性
?
事件屬性\事件 |
Startup/Shutdown |
Servererror |
Logon/Logoff |
DDL |
DML |
事件名稱 |
ü * |
ü * |
ü * |
ü * |
* |
數(shù)據(jù)庫名稱 |
ü * |
? |
? |
? |
? |
數(shù)據(jù)庫實(shí)例號(hào) |
ü * |
? |
? |
? |
? |
錯(cuò)誤號(hào) |
? |
ü * |
? |
? |
? |
用戶名 |
? |
? |
ü * |
* |
? |
模式對(duì)象類型 |
? |
? |
? |
ü * |
* |
模式對(duì)象名稱 |
? |
? |
? |
ü * |
* |
列 |
? |
? |
? |
? |
ü * |
?
除DML語句的列屬性外,其余事件屬性值可通過調(diào)用ORACLE定義的事件屬性函數(shù)來讀取。
函數(shù)名稱 |
數(shù)據(jù)類型 |
說??? 明 |
Ora_sysevent |
VARCHAR2(20) |
激活觸發(fā)器的事件名稱 |
Instance_num |
NUMBER |
數(shù)據(jù)庫實(shí)例名 |
Ora_database_name |
VARCHAR2(50) |
數(shù)據(jù)庫名稱 |
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_user |
VARCHAR2(30) |
登陸或注銷的用戶名稱 |
Dictionary_obj_type |
VARCHAR2(20) |
DDL語句所操作的數(shù)據(jù)庫對(duì)象類型 |
Dictionary_obj_name |
VARCHAR2(30) |
DDL語句所操作的數(shù)據(jù)庫對(duì)象名稱 |
Dictionary_obj_owner |
VARCHAR2(30) |
DDL語句所操作的數(shù)據(jù)庫對(duì)象所有者名稱 |
Des_encrypted_password |
VARCHAR2(2) |
正在創(chuàng)建或修改的經(jīng)過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ā)語句是 INSERT 語句,則為TRUE,否則為FALSE |
UPDATING |
如果觸發(fā)語句是 UPDATE語句,則為TRUE,否則為FALSE |
DELETING |
如果觸發(fā)語句是 DELETE 語句,則為TRUE,否則為FALSE |
?
8.2.6 重新編譯觸發(fā)器
如果在觸發(fā)器內(nèi)調(diào)用其它函數(shù)或過程,當(dāng)這些函數(shù)或過程被刪除或修改后,觸發(fā)器的狀態(tài)將被標(biāo)識(shí)為無效。當(dāng)DML語句激活一個(gè)無效觸發(fā)器時(shí),ORACLE將重新編譯觸發(fā)器代碼,如果編譯時(shí)發(fā)現(xiàn)錯(cuò)誤,這將導(dǎo)致DML語句執(zhí)行失敗。
在PL/SQL程序中可以調(diào)用ALTER TRIGGER語句重新編譯已經(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ù)庫上的觸發(fā)器時(shí),用戶需要具有ADMINISTER DATABASE TRIGGER系統(tǒng)權(quán)限。
此外,當(dāng)刪除表或視圖時(shí),建立在這些對(duì)象上的觸發(fā)器也隨之刪除。?
l????????? 禁用或啟用觸發(fā)器
數(shù)據(jù)庫TRIGGER 的狀態(tài):
有效狀態(tài)(ENABLE):當(dāng)觸發(fā)事件發(fā)生時(shí),處于有效狀態(tài)的數(shù)據(jù)庫觸發(fā)器TRIGGER 將被觸發(fā)。
無效狀態(tài)(DISABLE):當(dāng)觸發(fā)事件發(fā)生時(shí),處于無效狀態(tài)的數(shù)據(jù)庫觸發(fā)器TRIGGER 將不會(huì)被觸發(fā),此時(shí)就跟沒有這個(gè)數(shù)據(jù)庫觸發(fā)器(TRIGGER) 一樣。
數(shù)據(jù)庫TRIGGER的這兩種狀態(tài)可以互相轉(zhuǎn)換。格式為:
ALTER?TIGGER?trigger_name?[DISABLE?|?ENABLE?];
--例:ALTER?TRIGGER?emp_view_delete?DISABLE;
???????????
???????????ALTER TRIGGER語句一次只能改變一個(gè)觸發(fā)器的狀態(tài),而ALTER TABLE語句則一次能夠改變與指定表相關(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ù)庫觸發(fā)器的應(yīng)用舉例
例 1 : 創(chuàng)建一個(gè)DML語句級(jí)觸發(fā)器,當(dāng)對(duì)emp表執(zhí)行INSERT, UPDATE, DELETE 操作時(shí),它自動(dòng)更新dept_summary 表中的數(shù)據(jù)。由于在PL/SQL塊中不能直接調(diào)用DDL語句,所以,利用ORACLE內(nèi)置包DBMS_UTILITY中的EXEC_DDL_STATEMENT過程,由它執(zhí)行DDL語句創(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過程disp_dept_summary --在觸發(fā)器中調(diào)用該過程顯示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=1000 WHERE 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語句行級(jí)觸發(fā)器。當(dāng)對(duì)emp表執(zhí)行INSERT, UPDATE, DELETE 操作時(shí),它自動(dòng)更新dept_summary 表中的數(shù)據(jù)。由于在PL/SQL塊中不能直接調(diào)用DDL語句,所以,利用ORACLE內(nèi)置包DBMS_UTILITY中的EXEC_DDL_STATEMENT過程,由它執(zhí)行DDL語句創(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.1 WHERE 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.1 WHERE 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語句。根據(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|| ''的部門已存在,插入操作失敗!''); 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('部門編號(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ù)庫中所創(chuàng)建的數(shù)據(jù)庫對(duì)象,以及用戶的登陸和注銷、數(shù)據(jù)庫的啟動(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ù)庫觸發(fā)器的應(yīng)用實(shí)例
用戶可以使用數(shù)據(jù)庫觸發(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 WITH 1000 INCREMENT BY 1 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)的修改;
?
?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
