觸發器是許多關係數據庫系統都提供的一項技術。在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 觸發器
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;
行級(ROW)觸發器:是指當某觸發事件發生時,對受到該操作影響的每一行數據,觸發器都單獨執行一次。
實現: :NEW 修飾符訪問操作完成後列的值
:OLD 修飾符訪問操作完成前列的值
特性 |
INSERT |
UPDATE |
DELETE |
OLD |
NULL |
實際值 |
實際值 |
NEW |
實際值 |
實際值 |
NULL |
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);
*/
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;
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權限。
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;