觸發器詳解
第 13 章 開發觸發器
觸發器是指存放在資料庫中,並被隱含執行的儲存過程。在ORACLE8i之前,只允許基於表或檢視的DML操作(INSERT,UPDATE和DELETE)建立觸發器;而從ORACLE8i開始,不僅支援DML觸發器,也允許基於系統事件(啟動資料庫、關閉資料庫、登入)和DDL操作建立觸發器。
13.1 觸發器簡介
觸發器由 觸發事件、 觸發條件和 觸發操作三部分組成。
1 觸發事件
觸發事件是指引起觸發器被觸發的SQL語句、資料庫事件或使用者事件。在Oracle8i之前,觸發事件只能是DML操作;在8i開始,不僅支援原有的DML事件,而且還增加了其他觸發器事件。具體觸發事件如下:
(1) 啟動和關閉例程;
(2)Oracle 錯誤資訊;
(3) 使用者登入和斷開會話;
(4) 特定表或檢視的DML 操作;
(5) 在任何方案上的DDL 語句。
2 觸發條件( 可選)
觸發條件是指使用WHEN子句指定一個BOOLEAN表示式,當布林表示式返回值為TRUE時,會自動執行觸發器相應程式碼;當布林表示式返回值為FALSE或UNKNOWN時,不會執行觸發器。
3 觸發操作
觸發操作是指包含SQL語句和其他執行程式碼的PL/SQL塊,不僅可以使用PL/SQL進行開發,也可以使用JAVA語言和C語言進行開發。當觸發條件為TRUE時,會自動執行觸發器操作的相應程式碼。但編寫觸發器執行程式碼時,需要注意一下限制:
(1) 觸發器程式碼大小不能超過32K 。如果確實需要使用大量程式碼建立觸發器,應該首先建立儲存過程,然後在觸發器中使用CALL 語句呼叫儲存過程。
(2) 觸發器只能包含SELECT,INSERT,UPDATE 和DELETE 語句,而不能包含DDL 語句(CREATE,ALTER,DROP) 和事務控制語句(COMMIT,ROLLBACK 和SAVEPOINT) 。
13.2 建立DML 觸發器
在Oracle8i之前,只能基於DML事件建立觸發器。在建立了DML觸發器後,如果發生了相應的DML操作,就會自動執行觸發器的程式碼。當建立DML觸發器時,需要指定觸發時機(BEFORE或AFTER)、觸發事件(INSERT,UPDATE,DELETE)、表名、觸發型別、觸發條件以及觸發操作。
1 觸發時機
觸發時機用於指定觸發器的觸發事件。當指定BEFORE關鍵字時,表示在執行DML操作之前觸發觸發器;當指定AFTER關鍵字,表示在執行DML操作之後觸發觸發器。
2 觸發事件
觸發事件用於指定導致觸發器執行的DML操作,也即INSERT,UPDATE和DELETE操作。既可以使用單個觸發事件,也可以組合多個觸發事件。
3 表名
因為DML觸發器是針對特定表進行的,所以必須指定DML操作所對應的表。
4 觸發型別
觸發型別用於指定當觸發事件發生後,需要執行幾次觸發操作。如果指定語句觸發型別(預設),那麼只會執行一次觸發器程式碼;如果指定行觸發型別,則會在每個被作用行上執行一次觸發器程式碼。
5 觸發條件
觸發條件用於指定執行觸發器程式碼的條件,只有條件為TRUE時才會執行觸發器程式碼。注意,當編寫DML觸發器時,只允許在行觸發器上指定觸發條件。
6 觸發操作
觸發操作作用於指定觸發器執行程式碼。如果使用PL/SQL儲存過程、JAVA儲存過程或外部儲存過程來實現觸發器程式碼,那麼在觸發操作部分可直接使用CALL語句呼叫相應過程。如果使用PL/SQL匿名塊編寫觸發器操作,則應該按照以下格式進行編寫:
[DECLARE]
________定義變數、常量等
BEGIN
________編寫SQL語句和PL/SQL語句
EXCEPTION
________編寫例外處理語句
END;
7 DML 觸發器觸發順序
(1) DML觸發器在單行資料上的觸發順序
當針對某一表的相同DML操作而建立了多個DML觸發器(BEFORE/AFTER語句觸發器、BEFORE/AFTER行觸發器)時,如果在單行資料上執行了該種DML操作,則觸發器會按照以下順序執行:
對應單行資料而言,無論是語句觸發器,還是行觸發器,觸發器程式碼實際只執行一次,並且執行順序為BEFORE語句觸發器、BEFORE行觸發器、DML操作、AFTER行觸發器、AFTER語句觸發器。
(2) DML觸發器在多行資料上的觸發順序
在多行資料而言,語句觸發器只能執行一次,而行觸發器在每個作用行上都執行一次。
13.2.1 語句觸發器
語句觸發器是指當執行DML語句時被隱含執行的觸發器。如果在表上針對某種DML操作建立了語句觸發器,那麼當執行DML操作時會自動執行觸發器的相應程式碼。當審計DML操作,或者確保DML操作安全執行時,可以使用語句觸發器。注意,使用語句觸發器,不能記錄列資料的變化。建立語句觸發器的語法如下:
CREATE [OR REPLACE] TRIGGER trigger_name
timing event1 [OR event2 OR event3]
ON table_name
PL/SQL block;
如上所示,trigger_name用於指定觸發器名;timint用於指定觸發器時機(BEFORE或AFTER);event用於指定觸發器事件(INSERT、UPDATE和DELETE);table_name用於指定DML操作所對應的表名。
1 建立BEFORE 語句觸發器
為了確保DML操作在正常情況下執行,可以基於DML操作建立BEFORE語句觸發器。例如,為了禁止工作人員在休息日改變僱員資訊,開發人員可以建立BEFORE語句觸發器,以實現資料的安全保護。示例如下:
SQL> create or replace trigger tr_sec_emp
2 before insert or update or delete on emp
3 begin
4 if to_char(sysdate,'DY','nls_date_language=AMERICAN')
5 in('SAT','SUN') THEN
6 raise_application_error(-20001,'bu neng zai xiu xi ri xiu gai gu yuan xin xi');
7 end if;
8 end;
9 /
Trigger created.
在建立了觸發器tr_sec_emp之後,如果星期六、星期日在EMP表上執行DML操作,則會顯示錯誤資訊。示例如下:
[root@ogg1 ~]# date -s "20150531 14:05:30"
Sun May 31 14:05:30 CST 2015
[root@ogg1 ~]# date
Sun May 31 14:05:31 CST 2015
SQL> delete emp where deptno=10;
delete emp where deptno=10
*
ERROR at line 1:
ORA-20001: bu neng zai xiu xi ri xiu gai gu yuan xin xi
ORA-06512: ?"CHEN.TR_SEC_EMP", line 4
ORA-04088: 靠?'CHEN.TR_SEC_EMP' 靠靠靠?
2 使用條件謂語
當觸發器中同時包含多個觸發事件(INSERT,UPDATE,DELETE)時,為了在觸發器程式碼中區分具體的觸發器事件,可以使用以下三個條件謂語:
(1) INSERTINT:當觸發器事件是INSERT時,該條件謂語返回值為TRUE,否則為FALSE。
(2) UPDATING:當觸發器事件是UPDATE時,該條件謂語返回值為TRUE,否則為FALSE。
(3) DELETING:當觸發器事件是DELETE時,該條件謂語返回值為TRUE,否則為FALSE。
下面舉例說明在觸發器中使用這三個條件謂語的方法,示例如下:
SQL> create or replace trigger tr_sec_emp
before insert or update or delete on emp
begin
if to_char(sysdate,'DY','nls_date_language=AMERICAN')
IN('SAT','SUN') THEN
CASE
WHEN INSERTING THEN
raise_application_error(-20001,'not add people in rest day');
WHEN UPDATING THEN
raise_application_error(-20002,'not modify people in rest day');
WHEN DELETING THEN
raise_application_error(-20003,'not delete people in rest day');
end case;
end if;
end;
/
Trigger created.
SQL> insert into emp(empno,ename) values(123,'abc');
insert into emp(empno,ename) values(123,'abc')
*
ERROR at line 1:
ORA-20001: not add people in rest day
ORA-06512: ?"CHEN.TR_SEC_EMP", line 6
ORA-04088: 靠?'CHEN.TR_SEC_EMP' 靠靠靠?
SQL> update emp set sal=5000 where deptno=10;
update emp set sal=5000 where deptno=10
*
ERROR at line 1:
ORA-20002: not modify people in rest day
ORA-06512: ?"CHEN.TR_SEC_EMP", line 8
ORA-04088: 靠?'CHEN.TR_SEC_EMP' 靠靠靠?
SQL> delete emp where deptno=20;
delete emp where deptno=20
*
ERROR at line 1:
ORA-20003: not delete people in rest day
ORA-06512: ?"CHEN.TR_SEC_EMP", line 10
ORA-04088: 靠?'CHEN.TR_SEC_EMP' 靠靠靠?
3 建立AFTER 語句觸發器
為了審計DML操作,或者在DML操作之後執行彙總運算,可以使用AFTER語句觸發器。例如,為了審計在EMP表上INSERT,UPDATE和DELETE的操作次數,可以建立AFTER觸發器。在建立AFTER觸發器之前,首先建立審計表audit_table。示例如下:
SQL> create table audit_table(
2 name varchar2(20),ins int,upd int,del int,starttime date,endtime date);
Table created.
為了審計EMP表上DML操作執行的次數、最早執行時間和最近執行時間,需要建立AFTER語句觸發器。示例如下:
SQL> create or replace trigger tr_audit_emp
2 after insert or update or delete on emp
3 declare
4 v_temp int;
5 begin
6 select count(*) into v_temp from audit_table where name='EMP';
7 if v_temp=0 then
8 insert into audit_table values('EMP',0,0,0,SYSDATE,NULL);
9 end if;
10 case
11 when inserting then
12 update audit_table set ins=ins+1,endtime=SYSDATE where name='EMP';
13 when updating then
14 update audit_table set upd=upd+1,endtime=sysdate where name='EMP';
15 when deleting then
16 update audit_table set del=del+1,endtime=sysdate where name='EMP';
17 end case;
18 end;
19 /
Trigger created.
SQL> update emp set sal=5000 where empno=7788;
1 row updated.
SQL> update emp set sal=8000 where empno=7369;
1 row updated.
SQL> delete emp where empno=7369;
1 row deleted.
SQL> insert into emp(empno,ename) values(1234,'aaa');
1 row created.
SQL> select * from audit_table;
NAME INS UPD DEL STARTTIME ENDTIME
-------------------- ---------- ---------- ---------- ---------- ----------
EMP 1 2 1 02-6? -15 02-6? -15
13.2.2 行觸發器
行觸發器是指執行DML操作時,每作用一行就觸發一次的觸發器。審計資料變化時,可以使用行觸發器。建立行觸發器的語法如下:
CREATE [OR REPLACE] TRIGGER trigger_name
Timing event1 [OR event12OR event3]
ON table_name
[REFERENCING OLD AS old | NEW AS NEW]
FOR EACH ROW
[WHEN condition]
PL/SQL block;
其中:REFERENCING子句用於指定引用新、舊資料的方式,預設情況使用OLD修飾符引用舊資料,使用NEW修飾符引用新資料;FOR EACH ROW表示建立行觸發器;
1 建立BEFORE 行觸發器
在開發資料庫應用時,為了確保資料符合商業邏輯或企業規則,應該使用約束對輸入資料加以限制,但某些情況下使用約束可能無法實現複雜的商業邏輯或企業規則,此時可以考慮使用BEFORE行觸發器。下面以確保僱員工資不能低於其原有工資為例,說明建立BEFORE行觸發器。示例如下:
SQL> create or replace trigger tr_emp_sal
before update of sal on emp for each row
begin
if :new.sal<:old.sal then="">
raise_application_error(-20010,'Bu neng jiang sal!');
end if;
end;
/
Trigger created.
在建立觸發器tr_emp_sal之後,如果僱員新工資低於其原工資,則會提示錯誤資訊。示例如下:
SQL> update emp set sal=1000 where empno=7788;
update emp set sal=1000 where empno=7788
*
ERROR at line 1:
ORA-20010: Bu neng jiang sal!
ORA-06512: ?"CHEN.TR_EMP_SAL", line 3
ORA-04088: 靠?'CHEN.TR_EMP_SAL' 靠靠靠?
2 建立AFTER 行觸發器
為了審計DML操作,可以使用語句觸發器或ORACLE系統提供的審計功能;而為了審計資料變化,則應該使用AFTER行觸發器。下面以審計僱員工資變化為例,說明使用AFTER行觸發器的方法。在建立觸發器之前,首先應建立存放審計資料的表audit_emp_change,示例如下:
SQL> create table audit_emp_change(
2 name varchar2(10),oldsal number(6,2),newsal number(6,2),time date);
Table created.
為了審計所有僱員的工資變化和僱員工資的更新日期,必須要建立AFTER行觸發器。示例如下:
SQL> create or replace trigger tr_sal_change
after update of sal on emp
for each row
declare
v_temp int;
begin
select count(*) into v_temp from audit_emp_change where name=:old.ename;
if v_temp=0 then
insert into audit_emp_change values(:old.ename,:old.sal,:new.sal,sysdate);
else
update audit_emp_change set oldsal=:old.sal,newsal=:new.sal,time=sysdate where name=:old.ename;
end if;
end;
/
Trigger created.
在建立觸發器tr_sal_change之後,當修改僱員工資時,會將每個僱員的工資變化全部寫入到審計表audit_emp_change中。示例如下
SQL> update emp set sal=sal*1.5 where deptno=20;
5 rows updated.
SQL> select * from audit_emp_change;
NAME OLDSAL NEWSAL TIME
---------- ---------- ---------- ----------
SMITH 800 1200 03-6? -15
JONES 2975 4462.5 03-6? -15
SCOTT 3000 4500 03-6? -15
ADAMS 1100 1650 03-6? -15
FORD 3000 4500 03-6? -15
3 限制行觸發器
當使用行觸發器時,預設情況下會在每個被作用行行執行一次觸發器程式碼。為了使得在特定條件下執行行觸發器程式碼,就需要使用WHEN子句對觸發器條件加以限制。下面以審計崗位“SALESMAN”的僱員工資變化為例,說明限制行觸發器的方法。示例如下:
SQL>
create or replace trigger tr_sal_change
after update of sal on emp
for each row
when(old.job='CLERK')
declare
v_temp int;
begin
select count(*) into v_temp from audit_emp_change where name=:old.ename;
if v_temp=0 then
insert into audit_emp_change values(:old.ename,:old.sal,:new.sal,sysdate);
else
update audit_emp_change set oldsal=:old.sal,newsal=:new.sal,time=sysdate where name=:old.ename;
end if;
end;
/
Trigger created.
SQL> update emp set sal=sal*1.5 where deptno=20;
5 rows updated.
SQL> select * from audit_emp_change;
NAME OLDSAL NEWSAL TIME
---------- ---------- ---------- ----------
SMITH 800 1200 03-6? -15
ADAMS 1100 1650 03-6? -15
SQL> select ename,job from emp where deptno=20;
ENAME JOB
---------- ---------
SMITH CLERK
JONES MANAGER
SCOTT ANALYST
ADAMS CLERK
FORD ANALYST
4 DML 觸發器使用注意事項
當編寫DML觸發器時,觸發器程式碼不能從觸發器所對應的基表中讀取資料。例如,如果要基於EMP表建立觸發器,那麼該觸發器的執行程式碼不能包含對EMP表的查詢操作。儘管在建立觸發器時不會出現任何錯誤,但在執行相應觸發器操作時出顯示錯誤資訊。假定希望僱員工資不能超過當前僱員最高工資,並使用觸發器實現該規則。示例如下:
SQL> create or replace trigger tr_emp_sal
2 before update of sal on emp
3 for each row
4 declare
5 maxsal number(6,2);
6 begin
7 select max(sal) into maxsal from emp;
8 if :new.sal>maxsal then
9 raise_application_error(-20010,'chao chu shang xian');
10 end if;
11 end;
12 /
Trigger created.
如上所示,當建立觸發器tr_emp_sal時,不會顯示任何錯誤。但因為觸發器程式碼引用了基表emp,所以在執行UPDATE操作時會顯示如下錯誤資訊:
SQL> select max(sal) from emp;
MAX(SAL)
----------
5000
SQL> update emp set sal=3000 where empno=7369;
update emp set sal=3000 where empno=7369
*
ERROR at line 1:
ORA-04091: ?CHEN.EMP 靠靠? 靠?靠靠靠
ORA-06512: ?"CHEN.TR_EMP_SAL", line 4
ORA-04088: 靠?'CHEN.TR_EMP_SAL' 靠靠靠?
SQL> update emp set sal=8000 where empno=7369;
update emp set sal=8000 where empno=7369
*
ERROR at line 1:
ORA-04091: ?CHEN.EMP 靠靠? 靠?靠靠靠
ORA-06512: ?"CHEN.TR_EMP_SAL", line 4
ORA-04088: 靠?'CHEN.TR_EMP_SAL' 靠靠靠?
13.2.3 使用DML 觸發器
為了確保資料庫資料滿足特定的商業規則或企業邏輯,可以使用約束、觸發器和子程式實現。因為約束效能最好,實現最簡單,所有首先約束;如果使用約束不能實現特定規則,那麼應該選擇觸發器;如果觸發器仍然不能實現特定規則,那麼應該選擇子程式(過程和函式)。DML觸發器可以用於實現資料安全保護、資料審計、資料完整性、參照完整性、資料複製等功能,下面透過示例給大家說明如何實現這些功能。
1 控制資料安全
在伺服器級控制資料安全是透過授予和收回物件許可權來實現的,例如為了使得CHEN使用者可以在SCOTT.EMP表上執行DML操作和SELECT操作,必須要為CHEN使用者授予相應的物件許可權。如下所示:
SQL> conn scott/tiger
Connected.
SQL> grant select,insert,update,delete on emp to chen;
Grant succeeded.
當使用者有了以上許可權之後 ,就可以隨時在EMP表上執行相應的SQL操作。為了實現更復雜的安全模式(例如現在要修改的資料,修改時間等),就需要使用DML觸發器了。下面以限制使用者在正常工作時間(9:00~18:00)改變EMP表資料為例,說明使用DML觸發器控制資料安全的方法。示例如下:
SQL> create or replace trigger tr_emp_time
before insert or update or delete on emp
begin
if to_char(sysdate,'hh24') not between '9' and '18' then
raise_application_error(-20101,'fei gong zuo shi jian');
end if;
end;
/
Trigger created.
建立觸發器tr_temp_time之後,只能在 9:00~18:00之間在EMP表上執行DML操作。如果不在該時間段,則會顯示錯誤資訊:
SQL> update emp set sal=1200 where empno=7788;
update emp set sal=1200 where empno=7788
*
ERROR at line 1:
ORA-20101: fei gong zuo shi jian
ORA-06512: ?"CHEN.TR_EMP_TIME", line 3
ORA-04088: 靠?'CHEN.TR_EMP_TIME' 靠靠靠?
SQL> select to_char(sysdate,'hh24') from dual; --------Why???
TO
--
11
2 實現資料審計
審計用於監視非法和可疑的資料庫活動。Oracle資料庫本身提供了審計功能,例如,如果要對EMP表上的DML操作進行審計,可以執行如下命令:
SQL> audit insert,update,delete on emp by access;
SQL> show parameter audit
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
audit_file_dest string /u01/app/oracle/admin/ogg1/adu
mp
audit_sys_operations boolean FALSE
audit_syslog_level string
audit_trail string DB
SQL> conn /as sysdba
Connected.
SQL> alter system set audit_trail=db_extended scope=spfile;
System altered.
SQL> startup force
SQL> show parameter audit
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
audit_file_dest string /u01/app/oracle/admin/ogg1/adu
mp
audit_sys_operations boolean FALSE
audit_syslog_level string
audit_trail string DB_EXTENDED
SQL> conn chen/chen
Connected.
SQL> audit insert,update,delete on emp by access;
Audit succeeded.
SQL> update emp set sal=8000 where empno=7788;
1 row updated.
SQL> delete emp where depnto=10;
delete emp where depnto=10
*
ERROR at line 1:
ORA-00904: "DEPNTO": 靠靠?
SQL> delete emp where deptno=20;
5 rows deleted.
SQL> col sql_text for a45
SQL> select username,sql_text from dba_audit_trail where sql_text is not null;
USERNAME SQL_TEXT
------------------------------ ---------------------------------------------
CHEN update emp set sal=8000 where empno=7788
CHEN delete emp where depnto=10
CHEN delete emp where deptno=20
如上所示,在設定了審計選項之後,如果在EMP表上執行INSERT、UPDATE和DELETE操作,ORACLE會將關於SQL操作的資訊(使用者、時間等)寫入到資料字典中。 注意,使用資料庫審計只能審計 SQL 操作,而不會記載資料變化。為了審計SQL操作所引起的資料變化,必須要使用DML觸發器。示例如下:
SQL> create or replace trigger tr_sal_change
after update of sal on emp
for each row
declare
v_temp int;
begin
select count(*) into v_temp from audit_emp_change where name=:old.ename;
if v_temp=0 then
insert into audit_emp_change values(:old.ename,:old.sal,:new.sal,sysdate);
else
update audit_emp_change set oldsal=:old.sal,newsal=:new.sal,time=sysdate where name=:old.ename;
end if;
end;
/
Trigger created.
在建立了觸發器tr_sal_change之後,當修改僱員工資時,會將每個僱員的工資變化全部寫入到審計表audit_emp_change中。示例如下:
SQL> update emp set sal=sal*1.1 where deptno=30;
6 rows updated.
SQL> select * from audit_emp_change;
NAME OLDSAL NEWSAL TIME
---------- ---------- ---------- ----------
ALLEN 1600 1760 03-6? -15
WARD 1250 1375 03-6? -15
MARTIN 1250 1375 03-6? -15
BLAKE 2850 3135 03-6? -15
TURNER 1500 1650 03-6? -15
JAMES 950 1045 03-6? -15
6 rows selected.
SQL> select username,sql_text from dba_audit_trail where sql_text is not null;
USERNAME SQL_TEXT
------------------------------ ---------------------------------------------
CHEN update emp set sal=8000 where empno=7788
CHEN delete emp where depnto=10
CHEN delete emp where deptno=20
CHEN update emp set sal=sal*2 where deptno=10
CHEN update emp set sal=sal*1.1 where deptno=30
3 實現資料完整性
資料完整性用語確保資料庫資料滿足特定的商業邏輯或企業規則,資料完整性可以使用約束,觸發器和子程式實現。因為約束的實現最簡單,效能也最好,所以實現資料完整性首先約束。例如,為了限制僱員工資不能低於800元,可以選用CHENCK約束。示例如下:
SQL> alter table emp add constraint ck_sal check (sal>=800);
Table altered.
SQL> update emp set sal=8000 where empno=7369;
1 row updated.
SQL> update emp set sal=700 where empno=7788;
update emp set sal=700 where empno=7788
*
ERROR at line 1:
ORA-02290: 靠靠靠靠 (CHEN.CK_SAL)
但某些情況下使用約束無法實現特定的商業規則,此時可以使用觸發器來實現完整性。例如,假定希望僱員的新工資不能低於其原工資,並且不能高於原工資20%,使其約束顯然無法實現該規則,但透過觸發器卻可以實現該規則。示例如下:
SQL> create or replace trigger tr_check_sal
2 before update of sal on emp
3 for each row
4 when (new.sal1.2*old.sal)
5 begin
6 raise_application_error(-20931,'sal bu neng jiang,bu neng chao guo 20%');
7 end;
8 /
Trigger created.
SQL> update emp set sal=7999 where empno=7369;
update emp set sal=7999 where empno=7369
*
ERROR at line 1:
ORA-20931: sal bu neng jiang,bu neng chao guo 20%
ORA-06512: ?"CHEN.TR_CHECK_SAL", line 2
ORA-04088: 靠?'CHEN.TR_CHECK_SAL' 靠靠靠?
SQL> update emp set sal=16000 where empno=7369;
update emp set sal=16000 where empno=7369
*
ERROR at line 1:
ORA-20931: sal bu neng jiang,bu neng chao guo 20%
ORA-06512: ?"CHEN.TR_CHECK_SAL", line 2
ORA-04088: 靠?'CHEN.TR_CHECK_SAL' 靠靠靠?
SQL> update emp set sal=8500 where empno=7369;
1 row updated.
4 實現參照完整性
參照完整性是指若兩個表之間具有主從關係(也即主外來鍵關係),當刪除主表資料時,必須確保相關的從表資料已經被刪除;當修改主表的主鍵列資料時,必須確保相關從表資料已經被修改。為了實現級聯刪除,可以在定義外部鍵約束時指定ON DELETE CASCADE關鍵字。示例如下:
SQL>alter table emp drop constraint FK_DEPTNO;
SQL>alter table EMP
add constraint FK_DEPTNO foreign key (DEPTNO)
references DEPT (DEPTNO) on delete cascade;
當用如上方式建立了外部鍵fk_deptno之後,在刪除表DEPT的資料時,會同時刪除從表EMP的所有相關資料。但使用約束卻不能實現級聯更新,如果要更新DEPT表的部門號,則會顯示如下錯誤資訊:
SQL> update dept set deptno=50 where deptno=10;
update dept set deptno=50 where deptno=10
*
ERROR at line 1:
ORA-02292: integrity constraint (CHEN.FK_DEPTNO) violated - child record found
如上所示,錯誤原因是emp表包含有該部門的相應僱員。為了實現級聯更新,可以使用觸發器。示例如下:
SQL> create or replace trigger tr_update_cascade
2 after update of deptno on dept
3 for each row
4 begin
5 update emp set deptno=:new.deptno where deptno=:old.deptno;
6 end;
7 /
Trigger created.
在建立了觸發器tr_update_cascade之後,當更新DEPT表的部門號時,會級聯更新EMP表的相應僱員的部門號。示例如下:
SQL> update dept set deptno=50 where deptno=10;
1 row updated.
SQL> select * from dept;
DEPTNO DNAME LOC
---------- ---------------------------- --------------------------
50 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL> select ename,deptno from emp where deptno=50;
ENAME DEPTNO
-------------------- ----------
CLARK 50
KING 50
MILLER 50
13.3 建立INSTEAD OF 觸發器
對於簡單檢視,可以直接執行INSERT,UPDATE和DELETE操作。但對於複雜檢視,不允許直接執行INSERT,UPDATE和DELETE操作。 當檢視符合以下任何一種情況時,都不允許直接執行 DML 操作。具體情況如下:
1 具有集合運算子(UNION,UNION ALL,INTERSECT,MINUS);
2 具體分組函式(MIN,MAX,SUM,AVG,COUNT等);
3 具體GROUP BY,CONNECT BY或START WITH等子句;
4 具有DISTINCT關鍵字;
5 具有連線查詢。
為了在具有以上情況查詢的複雜檢視上執行DML操作,必須要基於檢視建立INSTEAD-OF觸發器。在建立INSTEAD-OF觸發器之後,就可以基於複雜檢視執行INSERT,UPDATE和DELETE語句。但建立INSTEAD-OF觸發器有以下注意事項:
1 INSTEAD OF 選項只適用於檢視;
2 當基於檢視建立觸發器時,不能指定BEFORE和AFTER選項;
3 在建立檢視時沒有指定WITH CHECK OPTION選項;
4 當建立INSTEAD OF觸發器時,必須指定FOR EACH ROW選項。
下面舉例說明覆雜檢視建立INSTEAD-OF觸發器的方法。
1 建立複雜檢視dept_emp
檢視是邏輯表,本身沒有任何資料。檢視只是對於一條SELECT語句,當查詢檢視時,其資料實際是從檢視基表上取得。為了簡化部門及其僱員資訊的查詢,應建立複雜檢視dept_emp。示例如下:
SQL> create or replace view dept_emp as
2 select a.deptno,a.dname,b.empno,b.ename from dept a,emp b where a.deptno=b.deptno;
View created.
SQL> select * from dept_emp where deptno=10;
DEPTNO DNAME EMPNO ENAME
---------- ---------------------------- ---------- --------------------
10 ACCOUNTING 7782 CLARK
10 ACCOUNTING 7839 KING
10 ACCOUNTING 7934 MILLER
SQL> insert into dept_emp values(50,'ADMIN','1223','MARY');
insert into dept_emp values(50,'ADMIN','1223','MARY')
*
ERROR at line 1:
ORA-01779: cannot modify a column which maps to a non key-preserved table
2 建立INSTEAD-OF 觸發器
為了在複雜檢視上執行DML操作,必須要基於複雜檢視建立INSTEAD-OF觸發器。下面以在複雜檢視dept_emp上執行INSERT操作為例,說明建立INSETEAD-OF觸發器的方法。示例如下:
SQL> create or replace trigger tr_instead_of_dept_emp
instead of insert on dept_emp
for each row
declare
v_temp int;
begin
select count(*) into v_temp from dept where deptno=:new.deptno;
if v_temp=0 then
insert into dept(deptno,dname) values(:new.deptno,:new.dname);
end if;
select count(*) into v_temp from emp where empno=:new.empno;
if v_temp=0 then
insert into emp(empno,ename,deptno) values(:new.empno,:new.ename,:new.deptno);
end if;
end;
/
Trigger created.
當建立了INSTEAD-OF觸發器tr_instead_dept_emp之後,就可以在複雜檢視dept_emp上執行INSERT操作了。示例如下:
SQL> insert into dept_emp values(50,'ADMIN','1223','MARY');
1 row created.
SQL> insert into dept_emp values(10,'ADMIN','1224','BAKE');
1 row created.
SQL> select * from dept;
DEPTNO DNAME LOC
---------- ---------------------------- --------------------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
50 ADMIN
SQL> select empno,ename,deptno from emp where empno in('1223','1224');
EMPNO ENAME DEPTNO
---------- -------------------- ----------
1223 MARY 50
1224 BAKE 10
13.4 建立系統事件觸發器
系統事件觸發器是指基於Oracle系統事件(例如LOGON和STARTUP)所建立的觸發器。透過使用系統事件觸發器,提供了跟蹤系統或資料庫變化的機制。下面介紹一下常用的系統事件屬性函式,以及建立各種事件觸發器的方法。
1 常用事件屬性函式
建立系統事件觸發器時,應用開發人員經常需要使用事件屬性函式。常用的事件屬性函式如下:
(1) ora_client_ip_address:用於返回客戶端的IP地址;
(2) ora_database_name:用於返回當前資料庫名;
(3) ora_des_encrypted_password:用於返回DES加密後的使用者口令;
(4) ora_dict_obj_name:用於返回DDL操作所對應的資料庫物件名;
(5) ora_dict_obj_name_list(name_list OUT ora_name_list_t):用於返回在事件中被修改的物件列表;
(6) ora_dict_obj_owner:用於返回DDL操作所對應的物件的所有者名;
(7) ora_dict_obj_owner_list(owner_list OUT ora_name_list_t):用於返回在事件中被修改物件的所有者列表;
(8) ora_dict_obj_type:用於返回DDL操作所對應的資料物件的型別;
(9) ora_grantee(user_list OUT ora_name_list_t):用於返回授權事件的授權者;
(10)ora_instance_num:用於返回例程號;
(11)ora_is_alter_column(column_name IN VARCHAR2):用於檢測定列是否被刪除;
(12)ora_is_creating_nested_table:用於檢測是否正在建立巢狀表;
(13)ora_is_drop_column(column_name IN VARCHAR2):用於檢測特定列是否被刪除;
(14)ora_is_servererro(error_number):用於檢測是否返回特定ORACLE錯誤;
(15)ora_login_user:用於返回登入使用者名稱;
(16)ora_sysevent:用於返回觸發器的系統事件名。
2 建立例程啟動和關閉觸發器
為了跟蹤例程啟動和關閉事件,可以分別建立例程啟動觸發器和例程關閉觸發器。為了記載例程啟動和關閉的事件和時間,首先建立事件表event_table。示例如下:
SQL> show user
USER is "CHEN"
SQL> create table event_table(event varchar2(20),time date);
Table created.
在建立了事件表event_table之後,就可以在觸發器中引用該表了。注意,例程啟動觸發器和例程關閉觸發器只能在特權使用者下才能建立,並且例程啟動觸發器只能使用AFTER關鍵字,而例程關閉觸發器只能使用BEFORE關鍵字,示例如下:
SQL> create or replace trigger tr_startup
2 after startup on database
3 begin
4 insert into event_table values(ora_sysevent,sysdate);
5 end;
6 /
Trigger created.
SQL> create or replace trigger tr_shutdown
2 before shutdown on database
3 begin
4 insert into event_table values(ora_sysevent,sysdate);
5 end;
6 /
Trigger created.
SQL> conn /as sysdba
Connected.
SQL> shutdown immediate
SQL> startup
SQL> conn chen/chen
SQL> select event,to_char(time,'yyyy-mm-dd hh24:mi:ss') time from event_table;
EVENT TIME
---------------------------------------- --------------------------------------
SHUTDOWN 2015-06-04 14:45:42
STARTUP 2015-06-04 14:45:58
[oracle@ogg1 ~]$ vim /u01/app/oracle/diag/rdbms/ogg1/ogg1/trace/alert_ogg1.log
Shutting down instance (immediate)
Stopping background process SMCO
Shutting down instance: further logons disabled
…………………
Thu Jun 04 14:45:51 2015
PMON started with pid=2, OS id=7159
…………………
3 建立登入和退出觸發器
為了記載使用者登入和退出事件,可以分別建立登入和退出觸發器。為了記載登入使用者和退出使用者的名稱、事件和IP地址,應該首先建立專門存放登入和退出的資訊表LOG_TABLE。示例如下:
SQL> create table log_table(
2 username varchar2(20),logon_time date,logoff_time date,address varchar2(20));
Table created.
在建立了LOG_TABLE表之後,就可以在觸發器中引用該表了。注意,登入觸發器和退出觸發器一定要以特權使用者身份建立,並且登入觸發器只能使用AFTER關鍵字,而退出觸發器只能使用BEFORE關鍵字。示例如下:
SQL> create or replace trigger tr_logon
2 after logon on database
3 begin
4 insert into log_table(username,logon_time,address) values(ora_login_user,sysdate,ora_client_ip_address);
5 end;
6 /
Trigger created.
SQL> create or replace trigger tr_logoff
2 before logoff on database
3 begin
4 insert into log_table(username,logoff_time,address) values(ora_login_user,sysdate,ora_client_ip_address);
5 end;
6 /
Trigger created.
在建立了觸發器tr_logon之後,當使用者登入到資料庫之後,會執行觸發器程式碼;在建立了觸發器tr_logoff之後,當使用者斷開資料庫連線之前,會執行其觸發器程式碼。示例如下:
SQL> conn scott/tiger
Connected.
SQL> conn oe/oe
Connected.
SQL> conn / as sysdba
Connected.
SQL> conn system/oracle
Connected.
SQL> conn chen/chen
Connected.
SQL> select * from log_table;
SQL> col username for a8
SQL> col address for a20
SQL> select * from log_table order by username;
USERNAME LOGON_TIME LOGOFF_TIME ADDRESS
-------- ------------ ------------ --------------------
CHEN 04-JUN-15
CHEN 04-JUN-15
OE 04-JUN-15
OE 04-JUN-15
SCOTT 04-JUN-15
SCOTT 04-JUN-15
SYS 04-JUN-15
SYS 04-JUN-15
SYSTEM 04-JUN-15
SYSTEM 04-JUN-15
4 建立DDL 觸發器
為了記載系統所發生的DDL事件(CREATE,ALTER,DROP等),可以建立DDL觸發器。為了記載DDL事件資訊,應該建立專門的表,以便存放DDL事件資訊。示例如下:
SQL> create table event_ddl(
2 event varchar2(20),username varchar2(10),
3 owner varchar2(10),objname varchar2(20),
4 objtype varchar2(10),time date);
Table created.
在建立表EVENT_DDL之後,就可以在觸發器中引用該表了。為了記載DDL事件,應該建立DDL觸發器。注意,當建立DDL觸發器時,必須要使用AFTER關鍵字。示例如下:
SQL> create or replace trigger tr_ddl
2 after ddl on chen.schema
3 begin
4 insert into event_ddl values(ora_sysevent,ora_login_user,ora_dict_obj_owner,ora_dict_obj_name,ora_dict_obj_type,sysdate);
5 end;
6 /
Trigger created.
在建立了觸發器tr_ddl之後,如果在CHEN方案物件上執行DDL操作,則會將該資訊記載到表event_table中。示例如下:
SQL> create table t1(id number);
SQL> alter table t1 add(name varchar2(20));
SQL> drop table t1 purge;
SQL> col event for a8
SQL> col owner for a8
SQL> col objname for a8
SQL> col objtype for a8
SQL> select * from event_ddl;
EVENT USERNAME OWNER OBJNAME OBJTYPE TIME
-------- -------- -------- -------- -------- ------------
CREATE CHEN CHEN T1 TABLE 04-JUN-15
ALTER CHEN CHEN T1 TABLE 04-JUN-15
DROP CHEN CHEN T1 TABLE 04-JUN-15
13.5 管理觸發器
1 顯示觸發器資訊
建立觸發器時,oracle會將觸發器資訊寫入到資料字典中,透過查詢資料字典檢視user_triggers,可以顯示當前使用者所包含的所有觸發器資訊。示例如下:
SQL> select trigger_name,status from user_triggers;
TRIGGER_NAME STATUS
------------------------------------------------------------ ----------------
TR_INSTEAD_OF_DEPT_EMP ENABLED
TR_UPDATE_CASCADE ENABLED
TR_LOGON ENABLED
TR_STARTUP ENABLED
TR_SHUTDOWN ENABLED
TR_LOGOFF ENABLED
TR_DDL ENABLED
7 rows selected.
2 禁止觸發器
禁止觸發器是指使觸發器臨時失效。當觸發器處於ENABLE狀態時,如果表上執行DML操作,則就會觸發相應的觸發器。如果基於INSERT操作建立觸發器,當使用SQL*Loader裝載大批次資料時會觸發觸發器。為了加快資料裝載速度,應該在裝載資料之前禁止觸發器。方法如下:
SQL> alter trigger tr_ddl disable;
Trigger altered.
SQL> select trigger_name,status from user_triggers where trigger_name='TR_DDL';
TRIGGER_NAME STATUS
------------------------------------------------------------ ----------------
TR_DDL DISABLED
3 啟用觸發器
啟用觸發器是指使觸發器重新生效。當使用SQL*Loader裝載了資料之後,為了使被禁止的觸發器生效,應該啟用觸發器。方法如下:
SQL> alter trigger tr_ddl enable;
4 禁止或啟用表的所有觸發器
SQL> alter table emp disable all triggers;
SQL> alter table emp enable all triggers;
5 重新編譯觸發器
當使用ALTER TABLE命令修改表結構(例如增加列、刪除列)時,會使得其觸發器轉變為INVALID狀態。在這種情況下,為了使得觸發器繼續生效,需要重新編譯觸發器。示例如下:
SQL> alter trigger tr_ddl compile;
6 刪除觸發器
當觸發器不在需要時,可以使用drop trigger命令刪除觸發。注意,在表上的觸發器越多,對應DML操作的效能影響越大,所以一定要適度使用觸發器。刪除觸發器的示例如下:
SQL> drop trigger tr_ddl;
----- 本文摘抄自《精通oracle 10g PL/SQL 程式設計( 王海亮) 》
歡迎關注我的微信公眾號"IT小Chen",共同學習,共同成長!!!
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29785807/viewspace-1687817/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- ORACLE觸發器詳解Oracle觸發器
- SQL Server:觸發器詳解SQLServer觸發器
- zabbix觸發器表示式詳解觸發器
- MySQL觸發器使用詳解MySql觸發器
- SQL Server 觸發器詳情HOPPSQLServer觸發器
- Oracle觸發器詳細介紹Oracle觸發器
- 瞭解SQL Server觸發器及觸發器中的事務AWSQLServer觸發器
- SQL觸發器例項講解SQL觸發器
- oralce觸發器解決問題觸發器
- Oracle觸發器觸發級別Oracle觸發器
- Oracle觸發器6(建立系統事件觸發器)Oracle觸發器事件
- 根據業務寫觸發器(oracle觸發器片)觸發器Oracle
- mysql——觸發器MySql觸發器
- mysql 觸發器MySql觸發器
- SQL觸發器SQL觸發器
- Mysql觸發器:MySql觸發器
- Oracle觸發器Oracle觸發器
- mysql觸發器MySql觸發器
- 數位電路-D觸發器詳解及其在編碼器解碼電路中的應用觸發器
- 【SQL Server】-- 一觸即發之觸發器SQLServer觸發器
- Oracle觸發器死鎖問題解決Oracle觸發器
- mysql繞過行觸發器,實現語句觸發器MySql觸發器
- sqlserver 列觸發器SQLServer觸發器
- 除錯觸發器除錯觸發器
- 建立MySQL觸發器MySql觸發器
- SqlServer-觸發器SQLServer觸發器
- MySQL使用觸發器MySql觸發器
- MySql-觸發器MySql觸發器
- MySQL 建立觸發器MySql觸發器
- mysql建立觸發器MySql觸發器
- SQL Server 觸發器SQLServer觸發器
- postgresql 觸發器操作SQL觸發器
- 建立SQL觸發器SQL觸發器
- ORACLE DDL觸發器Oracle觸發器
- SQL Server觸發器SQLServer觸發器
- 淺談觸發器觸發器
- Oracle之觸發器Oracle觸發器
- SQL觸發器(二)SQL觸發器