Oracle 觸發器

觸發器是許多關係數據庫系統都提供的一項技術。在ORACLE系統裏,觸發器類似過程和函數,都有聲明,執行和異常處理過程的PL/SQL塊。

編寫觸發器時,需要注意以下幾點:
觸發器不接受參數。
一個表上最多可有12個觸發器,但同一時間、同一事件、同一類型的觸發器只能有一個。並各觸發器之間不能有矛盾。
在一個表上的觸發器越多,對在該表上的DML操作的性能影響就越大。
觸發器最大爲32KB。若確實需要,可以先建立過程,然後在觸發器中用CALL語句進行調用。
在觸發器的執行部分只能用DML語句(SELECT、INSERT、UPDATE、DELETE),不能使用DDL語句(CREATE、ALTER、DROP)。DML語句手動commit,DDL語句自動commit.
觸發器中不能包含事務控制語句(COMMIT,ROLLBACK,SAVEPOINT)。因爲觸發器是觸發語句的一部分,觸發語句被提交、回退時,觸發器也被提交、回退了。
在觸發器主體中調用的任何過程、函數,都不能使用事務控制語句。
在觸發器主體中不能申明任何Long和blob變量。新值new和舊值old也不能向表中的任何long和blob列。
不同類型的觸發器(如DML觸發器、INSTEAD OF觸發器、系統觸發器)的語法格式和作用有較大區別。


DML 觸發器

 ORACLE可以在DML語句進行觸發,可以在DML操作前或操作後進行觸發,並且可以對每個行或語句操作上進行觸發。

1: 建立一個觸發器當職工表 emp 表被刪除一條記錄時,把被刪除記錄寫到職工表刪除日誌表中去。
SET SERVEROUTPUT ON;   --打開輸出顯示
CREATE TABLE emp_his AS SELECT * FROM EMP WHERE 1=2;  --複製表emp的結構到emp_his
CREATE OR REPLACE TRIGGER tr_del_emp 
   BEFORE DELETE --指定觸發時機爲刪除操作前觸發
   ON scott.emp 
   FOR EACH ROW   --說明創建的是行級觸發器 
BEGIN
   --將修改前數據插入到日誌記錄表 del_emp ,以供監督使用。
   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;

語句級(STATEMENT)觸發器:是指當某觸發事件發生時,該觸發器只執行一次;
行級(ROW)觸發器:是指當某觸發事件發生時,對受到該操作影響的每一行數據,觸發器都單獨執行一次。


在行觸發器的PL/SQL塊和WHEN 子句中可以使用相關名稱參照當前的新、舊列值,默認的相關名稱分別爲OLDNEW。觸發器的PL/SQL塊中應用相關名稱時,必須在它們之前加冒號(:).

實現:  :NEW 修飾符訪問操作完成後列的值

        :OLD 修飾符訪問操作完成前列的值

特性

INSERT

UPDATE

DELETE

OLD

NULL

實際值

實際值

NEW

實際值

實際值

NULL



2限制對Departments表修改(包括INSERT,DELETE,UPDATE)的時間範圍,即不允許在非工作時間修改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, '不是上班時間,不能修改departments表');
 END IF;
END;

3限定只對部門號爲80的記錄進行行觸發器操作。
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的人員的獎金不能降');
        END IF;
     WHEN DELETING THEN
          RAISE_APPLICATION_ERROR(-20003, '不能刪除部門80的人員記錄');
     END CASE;
END; 

/*
實例:
UPDATE employees SET salary = 8000 WHERE employee_id = 177;
DELETE FROM employees WHERE employee_id in (177,170);
*/

4利用行觸發器實現級聯更新。在修改了主表regions中的region_id之後(AFTER),級聯的、自動的更新子表countries表中原來在該地區的國家的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在觸發器中調用過程
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;

--創建觸發器調用存儲過程...
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;

 觸發器觸發次序

1.        執行 BEFORE語句級觸發器;

2.        對與受語句影響的每一行:

            執行 BEFORE行級觸發器  執行 DML語句 執行 AFTER行級觸發器 

3.        執行 AFTER語句級觸發器


系統事件觸發器

創建觸發器的一般語法是:
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:一個或多個DDL 事件,事件間用 OR 分開;
database_event_list:一個或多個數據庫事件,事件間用 OR 分開;

系統事件觸發器既可以建立在一個模式上,又可以建立在整個數據庫上。當建立在模式(SCHEMA)之上時,只有模式所指定用戶的DDL操作和它們所導致的錯誤才激活觸發器, 默認時爲當前用戶模式。當建立在數據庫(DATABASE)之上時,該數據庫所有用戶的DDL操作和他們所導致的錯誤,以及數據庫的啓動和關閉均可激活觸發器。要在數據庫之上建立觸發器時,要求用戶具有ADMINISTER DATABASE TRIGGER權限。

例1:創建觸發器,存放有關事件信息。
DESC ora_sysevent
DESC ora_login_user

--創建用於記錄事件用的表

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));

--創建觸犯發器
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:創建登錄、退出觸發器。
CREATE TABLE log_event
(user_name VARCHAR2(10),
 address VARCHAR2(20), 
 logon_date timestamp,
 logoff_date timestamp); 

--創建登錄觸發器
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;
--創建退出觸發器
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;

觸發器管理

重新編譯觸發器
如果在觸發器內調用其它函數或過程,當這些函數或過程被刪除或修改後,觸發器的狀態將被標識爲無效。當DML語句激活一個無效觸發器時,ORACLE將重新編譯觸發器代碼,如果編譯時發現錯誤,這將導致DML語句執行失敗。
在PL/SQL程序中可以調用ALTER TRIGGER語句重新編譯已經創建的觸發器,格式爲:         
ALTER TRIGGER [schema.] trigger_name COMPILE [ DEBUG]  
 其中:DEBUG 選項要器編譯器生成PL/SQL 程序條使其所使用的調試代碼。

刪除和禁止啓用觸發器
 刪除觸發器:
DROP TRIGGER trigger_name;
當刪除其他用戶模式中的觸發器名稱,需要具有DROP ANY TRIGGER系統權限,當刪除建立在數據庫上的觸發器時,用戶需要具有ADMINISTER DATABASE TRIGGER系統權限。
此外,當刪除表或視圖時,建立在這些對象上的觸發器也隨之刪除。 

禁用或啓用觸發器
數據庫TRIGGER 的狀態:
有效狀態(ENABLE):當觸發事件發生時,處於有效狀態的數據庫觸發器TRIGGER 將被觸發。
無效狀態(DISABLE):當觸發事件發生時,處於無效狀態的數據庫觸發器TRIGGER 將不會被觸發,此時就跟沒有這個數據庫觸發器(TRIGGER) 一樣。

數據庫TRIGGER的這兩種狀態可以互相轉換。格式爲:
ALTER TIGGER trigger_name [DISABLE | ENABLE ];

--例:ALTER TRIGGER emp_view_delete DISABLE;

 ALTER TRIGGER語句一次只能改變一個觸發器的狀態,而ALTER TABLE語句則一次能夠改變與指定表相關的所有觸發器的使用狀態。格式爲:          
ALTER TABLE [schema.]table_name {ENABLE|DISABLE} ALL TRIGGERS;

--例:使表EMP 上的所有TRIGGER 失效:
ALTER TABLE emp DISABLE ALL TRIGGERS; 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章