在ORACLE系統里,觸發器類似函數和過程。
1、觸發器類型:(一般為:語句級觸發器和行級觸發器。)
1)、DML觸發器: 創建在表上,由DML事件引發
2)、instead of觸發器: 創建在視圖上並且只能在行級上觸發,用於替代insert,delete等操作(由於oracle中不能直接對有兩個以上的表建立的視圖進行DML操作,所以給出替代觸發器,它是專門為進行視圖操作的一種處理方法)
3)、DDL觸發器: 觸發事件時數據庫對象的創建和修改
4)、數據庫事件觸發器:定義在數據庫或者模式上,由數據庫事件觸發
語法:
create or replace trigger 觸發器名
<before | after | instead of> <insert | update | delete>--<insert | update | delete>可以選擇一個或多個DML語句,如果選擇多個,則用or分開,如:insert or update。
on 表名或者視圖名或者用戶名或者數據庫名
[for each row] [觸發級別]針對一個表或視圖創建trigger時分為statement級別與row級別的trigger.所謂statement級別是說一個sql語句觸發一次trigger,而如果是row級別則一個sql語句涉及到多行數據則trigger會被觸發多次
when(condition)[when判斷條件]
[declare 變量] 11g
begin
pl/sql語句;
end 觸發器名;
(注:代碼涉及到對關聯表的數據操作,在行級觸發器中使用,將會報ORA-04091錯誤。這時可以通過自制事務來規避,弊端是使用自制事務后無法回滾rollback)
觸發時間:before/after 在指定的事件發生之前/后執行觸發器 instead of 觸發器用在對視圖的更新上。
觸發事件:Insert,update,delete,create(創建對象時),alter,drop,logon/logoff(用戶的登錄或注銷時執行觸發器),startup/shutdown(數據庫打開或關閉時執行觸發器)。
常見的是DML(insert,update,delete) , DDL(create,alter,drop)語句
觸發級別:for each row(行級觸發)對觸發事件影響的每一行執行觸發器,即觸發機制是基於行的。改一行數據,觸發一次。
不寫為語句觸發(對觸發事件只能觸發一次,而且不能該問受觸發器影響的每一行的值。既無論這條SQL語句影響多少條記錄,觸發器都只觸發一次。)
WHEN后跟的condition:是觸發器的響應條件,只對BEFORE和AFTER行級觸發器有效,當操作的記錄滿足condition時,觸發器才被執行,否則不執行。Condition中可以通過new對象和old對象不帶“:”(注意區別於前面的:new和:old,在代碼中引用需要加上冒號)來引用操作的記錄。
2、簡例
創建測試用表:
create table emp_bak1 as select * from emp;
create table emp_bak2 as select * from emp;
例1:
create or replace trigger insert_emp after insert on emp_bak1 for each row declare --Declare后面跟的是本地變量定義部分,如果沒有本地變量定義,此部分可以去掉 test_val emp_bak1.ename%type;--根據表的字段定義變量類型 begin dbms_output.put_line('員工編號:'||:new.empno); dbms_output.put_line('員工姓名:'||:new.ename); dbms_output.put_line('職位:'||:new.job); dbms_output.put_line('工資:'||:new.sal); dbms_output.put_line('所在部門:'||:new.deptno); dbms_output.put_line('---觸發器已被執行---'); --select ename into test_val from emp_bak1 where empno = 7839;--ORA-04091 select :new.empno into test_val from dual; dbms_output.put_line('---測試--->'||test_val); end; insert into emp_bak1 (empno, ename, job, sal, deptno) values ('666', 'SM', 'IT', '800', '20');
oracle默認的 用old代表老數據 new代表新數據(這兩個變量只有在使用了關鍵字 "FOR EACH ROW"時才存在;referencing new as new_val old as old_val:這個可以更改新舊值的名字來引用新值,舊值)
insert時 只有new 沒有old
delete時 只有old 沒有new
update時 二者都可用
create or replace trigger delete_emp before delete on emp_bak1 --刪除操作前觸發 for each row begin insert into emp_bak2 (empno, ename, job, sal, deptno) values (:old.empno, :old.ename, :old.job, :old.sal, :old.deptno); dbms_output.put_line('-----有數據刪除,員工號為'||:old.empno||'詳細信息見emp_bak2表------'); end delete_emp; insert into emp_bak1 (empno, ename, job, sal, deptno) values ('777', 'K', 'TE', '1000', '30'); delete from emp_bak1 where empno = 777;
例2:指定條件列觸發
create or replace trigger tri_01 after insert or update of sal on emp_bak1 for each row begin if inserting then dbms_output.put_line('新人入駐有收入sal'); elsif updating then dbms_output.put_line('sal有變動'); end if; end tri_01; update emp_bak1 set sal = 888 where empno = 7934;--觸發 update emp_bak1 set comm = 666 where empno = 7934;--未觸發
注:第二行中的of sal on emp_bak1 是在表emp_bak1的sal字段發生變更時才觸發操作,
在觸發器主體的if語句表達式中,inserting, updating和deleting可以用來區分當前是在做哪一種DML操作,可以作為把多個類似觸發器合並在一個觸發器中判別觸發事件的屬性。
例3:when條件限制
create or replace trigger tri_02 before update of sal, comm or delete on emp_bak1 for each row when (old.deptno = 30) --注:old前不帶':'且結尾不帶';' begin case when updating('sal') then dbms_output.put_line('原來薪資:'||:old.sal||';變更后薪資:'||:new.sal||';'); if :new.sal < :old.sal then dbms_output.put_line('降薪不被允許'); raise_application_error(-20001, '部門30的人員的工資不能降'); end if; when updating('comm') then dbms_output.put_line('原來獎金:'||:old.comm||';變更后獎金:'||:new.comm||';'); if :new.comm < :old.comm then dbms_output.put_line('降獎金不被允許'); raise_application_error(-20002, '部門30的人員的獎金不能降'); end if; when deleting then raise_application_error(-20003, '部門30的人員不能刪'); end case; end;
select * from emp_bak1 where deptno = 30;
update emp_bak1 set comm = 2000 where empno = 7499;
update emp_bak1 set sal = 20000 where empno = 7698;
raise_application_error相當於拒絕了插入或者修改事務
oracle允許自定義的錯誤代碼的范圍為-20000-->20999
raise_application_error(-20001,'ErrorCode');
例4:instead of觸發器
instead of 選項使ORACLE激活觸發器,而不執行觸發事件。只能對視圖和對象視圖建立instead of觸發器,而不能對表、模式和數據庫建立instead of 觸發器。
--語法類似: create [or replace] trigger trigger_name instead of {insert | delete | update [of column [, column …]]} on view_name --只能定義在視圖上 [referencing {old [as] old_val | new [as] new_val| parent as parent}]--可以給old/new對象賦予新的名稱(可以不要) [for each row ] --因為instead of觸發器只能在行級上觸發,所以沒有必要指定 [when condition] pl/sql_block | call procedure_name;
視圖創建
create view view_emp as select deptno,count(*) count_no,sum(sal) sal from emp group by deptno;
對基表處理過的視圖,在刪除時會報下面的錯誤
ORA-01732: 此視圖的數據操縱操作非法
此時可以創建INSTEAD_OF觸發器來為 DELETE 操作執行所需的處理,即刪除EMP表中所有基准行
create or replace trigger view_tri_03 instead of delete on view_emp for each row begin delete from emp where deptno= :old.deptno; dbms_output.put_line('從emp基表中刪除部門編號為:'||:old.deptno||'的基礎人員數據。'); end view_tri_03;
可看出此觸發器只是觸發了一個刪除部門號的事件,實際更改的還是基表。
例5:語句級觸發器
create or replace trigger tri_04 after insert or update of sal on emp_bak1 declare v_sumsal number; begin select sum(sal) into v_sumsal from emp_bak1; if v_sumsal > 50000 then raise_application_error(-20001, '總工資超過50000'); end if; end;
--如果用行級觸發器(for each row)會報ORA-04091錯誤
insert into emp_bak1 (empno, ename, job, sal, deptno) values ('777', 'K', 'TE', '90000', '30');
(注:此為學習記錄筆記,僅供參考若有問題請指正,后續補充......)
參考資料:https://www.cnblogs.com/wishyouhappy/p/3665851.html
參考資料:https://blog.csdn.net/weiwenhp/article/details/9179891
參考資料:https://www.cnblogs.com/hyq0002013/p/6085981.html
參考資料:https://blog.csdn.net/IndexMan/article/details/8023740