target
了解什么是觸發(fā)器
了解觸發(fā)器的作用及觸發(fā)器的類型
掌握觸發(fā)器的創(chuàng)建
掌握觸發(fā)器的修改及查看
1. 什么是觸發(fā)器
學習存儲過程后,認識和了解觸發(fā)器并不復(fù)雜,它們之間有著相似之處。
1.1 認識觸發(fā)器
觸發(fā)器和存儲過程比較類似,它由PL/SQL編寫并存儲在數(shù)據(jù)庫中,它可以調(diào)用存儲過程,但觸發(fā)器本身的調(diào)用和存儲過程調(diào)用卻是不一樣的。
存儲過程由用戶、應(yīng)用程序、觸發(fā)器或其他過程調(diào)用。但觸發(fā)器只能由數(shù)據(jù)庫的特定事件來觸發(fā)。所謂的特定事件主要包括如下幾種類型的事件。
(1) 用戶在指定的表或視圖中做DML操作,主要包括如下幾種:
- INSERT操作,在特定的表或視圖中増加數(shù)據(jù)。
- UPDATE操作,對特定的表或視圖修改數(shù)據(jù)。
- DELETE操作,刪除特定表或視圖的數(shù)據(jù)。
(2) 用戶做DDL操作,主要包括如下幾種:
- CREATE操作,創(chuàng)建對象。
- ALTER操作,修改對象。
- DROP操作,刪除對象。
(3) 數(shù)據(jù)庫事件,主要包括如下幾種:
- LOGON/LOGOFF用戶的登錄或注銷。
- STARTUP/SHUTDOWN數(shù)據(jù)庫的打開或關(guān)閉?
- ERRORS特定的錯誤消息等。
在以上事件中的一種或多種發(fā)生時就能使觸發(fā)器運行。
??:當員工表中新增一條記錄后,自動打印“成功插入新員工”
create or replace trigger insertStaffHint
after insert on emp_temp
for each row
declare
begin
dbms_output.put_line('新增員工成功');
end insertStaffHint;
/
測試:
SQL> set serveroutput on;
SQL> insert into emp_temp(empno,ename,job,sal,deptno) values(20,'韓梅梅','boss',12345,10);
新增員工成功
已創(chuàng)建 1 行。
1.2 觸發(fā)器的作用
觸發(fā)器可以根據(jù)不同的事件進行調(diào)用,它有著更加精細的控制能力,這種特性可以幫助開 發(fā)人員完成很多普通PL/SQL語句完成不了的功能。
自動生成自增長字段。例如,在表中插入數(shù)據(jù)前得到序列的最大值,避免序列重復(fù)。
執(zhí)行更復(fù)雜的業(yè)務(wù)邏輯。
防止無意義的數(shù)據(jù)操作。利用觸發(fā)器可以把符合某些條件的數(shù)據(jù)加以限制,使其不能變動。
提供審計。利用觸發(fā)器可以跟蹤對數(shù)據(jù)庫的操作,也可以在指定的表或視圖記錄改變時,利用觸發(fā)器把數(shù)據(jù)變動日志記錄下來。
允許或限制修改某些表。利用觸發(fā)器可以限制表的變動。
實現(xiàn)完整性規(guī)則。當一個表中的數(shù)據(jù)有變動時可以利用觸發(fā)器修改這些變動數(shù)據(jù)在其他表中的關(guān)聯(lián)數(shù)據(jù)(正常情況下可以利用外鍵進行限制)。
保證數(shù)據(jù)的同步復(fù)制。
建議開發(fā)人員只在必要時使用觸發(fā)器,因為觸發(fā)器可能造成比較雜的相關(guān)依賴性, 注意這種情況在大型的數(shù)據(jù)庫中可能會帶來麻煩。
1.3 觸發(fā)器的類型
觸發(fā)器可分為5種類型,具體內(nèi)容如下:
-
數(shù)據(jù)操縱語言(DML)觸發(fā)器。
此種類型觸發(fā)器定義到表上,當對表執(zhí)行INSERT、 UPDATE、DELETE操作時可以激發(fā)該類型的觸發(fā)器。利用該類觸發(fā)器可以復(fù)制、檢査、 替換某種符合指定條件的數(shù)據(jù)。
按照觸發(fā)級別可以分為兩種方式:
- 第一種為行級觸發(fā)器,此種類型表示每條記錄修改時都會激發(fā)該觸發(fā)器。
- 第二種為語句級觸發(fā)器,此種類型表示當SQL語句執(zhí)行時會激發(fā)該觸發(fā)器,與修改多少條記錄沒有關(guān)系。
按照數(shù)據(jù)的更改事件為準,則分為:
- BEFORE
- AFTER
-
數(shù)據(jù)定義語言(DDL)觸發(fā)器。
當CREATE、ALTER、DROP模式對象時會觸發(fā)相關(guān)的觸發(fā)器,在Oracle中可以簡單地理解一個用戶就有一個和它同名的模式,利用它可以使得某些表不能被修改或刪除。
復(fù)合觸發(fā)器。此種類型的觸發(fā)器是Oracle 11g的新特性,它相當于在一個觸發(fā)器中包含 了4種類型的觸發(fā)器,其中包含了BEFORE類型的語句級、BEFORE類型的行級、 AFTER類型的語句級、AFTER類型的行級。這種把所有觸發(fā)器都放到一個代碼塊中的 做法使得變量的傳遞變得更加方便。
INSTEAD OF觸發(fā)器。此種類型觸發(fā)器通常作用在視圖上。對由多個源表的視圖做 DML操作通常是不被允許的,如果遇到這種情況就可以利用INSTEAD OF類型觸發(fā)器 解決問題。利用它可以把對視圖的DML操作轉(zhuǎn)換成對多個源表進行操作。
用戶和系統(tǒng)事件觸發(fā)器。作用在數(shù)據(jù)庫上由數(shù)據(jù)庫事件激發(fā)的觸發(fā)器,如登錄和注銷 事件的觸發(fā)器。利用它可以記錄數(shù)據(jù)庫的登錄情況。
1.4 觸發(fā)器的語法
(1) DML觸發(fā)器語法
CREATE [ OR REPLACE ] TRIGGER [schema.] trigger
{BEFORE | AFTER | INSTEAD OF}
{DELETE | INSERT | UPDATE
[OF column (, column ]...]
}
[OR {DELETE | INSERT | UPDATE
[OF column [,column]...]
}
]...
{ON [schema. ]table | [schema.] view}
[FOR EACH ROW ]
[FOLLOWS [schema.] trigger [, [ schema. ] trigger ]...]
[ENABLE | DISABLE]
[WHEN (condition)]
trigger_body
【語法說明】
- OR REPLACE:新建的觸發(fā)器可以覆蓋原有同名觸發(fā)器。
- TRIGGER:創(chuàng)建觸發(fā)器的關(guān)鍵詞。
- schema:觸發(fā)器所屬模式(可簡單看成用戶名),如果不加該項則表示該觸發(fā)器屬于自己。
- BEFORE:觸發(fā)器類型為前觸發(fā)。
- AFTER:觸發(fā)器類型為后觸發(fā)。
- INSTEAD OF:表示觸發(fā)器類型為替換類型。
- DELETE I INSERT I UPDATE:表示觸發(fā)的事件。
- [ OF column [.column ]:觸發(fā)條件具體到的某列。
- ON [ schema. ] table I [ schema. ] view:該觸發(fā)器作用的表或視圖,INSTEAD OF類型可 以作用在視圖上。
- FOR EACH ROW:表示行級觸發(fā)器,省略則為語句級觸發(fā)器。
- FOLLOWS [ schema. ] trigger:觸發(fā)器執(zhí)行的順序。
- ENABLE I DISABLE:設(shè)置觸發(fā)器是否可用狀態(tài)。
- WHEN (condition):觸發(fā)該觸發(fā)器的條件。
- trigger_body:表示觸發(fā)器的函數(shù)體。
(2) DDL和數(shù)據(jù)庫事件觸發(fā)器語法
create [or replace] trigger [schema.] trigger
{ BEFORE | AFTER }
{ ddl_event [OR ddl_event]...
| database_event [OR database_event]...
}
ON { [schema.] SCHEMA
| DATABASE
}
[FOLLWS [schema.] trigger [,[schema.] trigger ]...]
[ENABLE | DISABLE]
[WHEN (condition)]
trigger_body
【語法說明】
- OR REPLACE;新建的觸發(fā)器可以覆蓋原有同名觸發(fā)器。
- TRIGGER:創(chuàng)建觸發(fā)器的關(guān)鍵詞。
- schema:觸發(fā)器所屬模式,如果不加該項則表示該觸發(fā)器屬于自己。
- BEFORE:觸發(fā)器類型為前觸發(fā)。
- AFTER:觸發(fā)器類型為后觸發(fā)。
- ddl.event |OR ddl_event]: DDL事件,用OR連接
- database_event[OR database_event|:數(shù)據(jù)庫事件,用OR連接。
- [schema.] SCHEMA I DATABASE:觸發(fā)器可作用在模式上或數(shù)據(jù)庫上。
- FOLLOWS [ schema. ] trigger:觸發(fā)器執(zhí)行的順序。
- ENABLE I DISABLE;設(shè)置觸發(fā)器是否可用狀態(tài)。
- WHEN (condition):觸發(fā)該觸發(fā)器的條件。
- trigger_body:表示觸發(fā)器的函數(shù)體。
部門DDL事件:
| DDL事件 | 簡介 |
|---|---|
| ALTER | 修改對象,例如修改對象的名稱約束等 |
| ANALYSE | 用來分析統(tǒng)計信息 |
| AUDIT/NOAUDIT | 啟用或取消審計 |
| COMMENT | 注解列或表的含義 |
| CREATE | 創(chuàng)建對象 |
| DROP | 刪除對象 |
| GRANT | 授權(quán)操作 |
| RENAME | 修改對象名稱 |
| REVOKE | 取消授權(quán) |
| TRUNCATE | 刪除整張表的行記錄 |
數(shù)據(jù)庫事件列表:
| 數(shù)據(jù)庫事件 | 簡介 |
|---|---|
| STARTUP | 數(shù)據(jù)庫打開后被觸發(fā),模式下不可以 |
| SHUTDOWN | 數(shù)據(jù)庫關(guān)閉前被觸發(fā),模式下不可以 |
| LOGON | 客戶程序登錄后觸發(fā) |
| LOGOFF | 客戶程序注銷前觸發(fā) |
| SERVERERROR | 錯誤消息出現(xiàn)后觸發(fā) |
(3) 復(fù)合觸發(fā)器語法
CREATE [OR REPLACE] TRIGGER schema.] trigger
FOR
{ DELETE | INSERT | UPDATE
[OF column [, column ]...]
}
[OR {DELETE | INSERT | UPDATE
[OF column (, column]... ]
}
]...
ON {(schema.]table
| [schema.] view
}
COMPOUND TRIGGER
{ BEFORE STATEMENT IS tps_body END BEFORE STATEMENT]
| BEFORE EACH ROW IS tps_body END BEFORE EACH ROW
| AFTER STATEMENT S tps body END AFTER STATEMENT
| AFTER EACH ROW S tps_body END AFTER EACH ROW
}
【語法說明】
- OR REPLACE:新建的觸發(fā)器可以覆蓋原有同名觸發(fā)器。
- TRIGGER:創(chuàng)建觸發(fā)器的關(guān)鍵詞。
- schema:觸發(fā)器所屬模式,如果不加該項則表示該觸發(fā)器屬于自己。
- DELETE | INSERT | UPDATE:表示觸發(fā)事件。
- COMPOUND TRIGGER:定義觸發(fā)器時表示為復(fù)合類型觸發(fā)器。
- BEFORE STATEMENT:前語句級觸發(fā)。
- BEFORE EACH ROW:前行級觸發(fā)。
- AFTER STATEMENT:后語句級觸發(fā)。
- AFTER EACH ROW:后行級觸發(fā)。
- tps_body:具體語句或程序。
2. 利用SQL*Plus創(chuàng)建觸發(fā)器
2.1 創(chuàng)建觸發(fā)器
用戶模式下如果想在自己的對象上創(chuàng)建觸發(fā)器, 則必須具有CREATE TRIGGER系統(tǒng)權(quán)限,如果想在其他用戶上創(chuàng)建觸發(fā)器,則需要有CREATE ANY TRIGGER權(quán)限。
除此之外,如果在數(shù)據(jù)庫上創(chuàng)建觸發(fā)器,則需要有ADMINISTER DATABASE TRIGGER系統(tǒng)權(quán)限。
??:刪除emp_temp表時觸發(fā)
create trigger emp_tri
--觸發(fā)類型為后觸發(fā),觸發(fā)事件是刪除操作,作用在emp_temp表上
after delete on emp_temp
begin
if deleting then
dbms_output.put_line('刪除數(shù)據(jù)操作');
end if;
end;
/
測試:
SQL> delete from emp_temp where empno = 20;
刪除數(shù)據(jù)操作
已刪除 1 行。
2.2 查看觸發(fā)器
(1) 查看觸發(fā)器名稱
select object_name from user_objects
where object_type = 'TRIGGER';
(2) 查看觸發(fā)器內(nèi)容
select * from user_source where name='EMP_TRI' order by line;
2.3 DML類型觸發(fā)器
dml類型觸發(fā)器在日常開發(fā)中比較常用。
當在 productinfo 表中增加數(shù)據(jù)時將觸發(fā)該觸發(fā)器,并把所做的操作記錄到表 option_log 中。
① 創(chuàng)建操作事件記錄表
| 字段名 | 注釋 | 數(shù)據(jù)類型 |
|---|---|---|
| id | 記錄id,主鍵 | varchar2(10) |
| oper_table | 被操作的表名 | varchar2(20) |
| oper_table_prk | 被操作表的主鍵 | varchar2(50) |
| oper_kd | 操作類型 | varchar2(10) |
| oper_date | 操作時間 | date |
SQL:
create table log_tab(
id varchar2(10),
oper_table varchar2(20),
oper_table_prk varchar2(50),
oper_kd varchar2(10),
oper_date date
);
② 創(chuàng)建用作 log_tab 表主鍵的自增長序列
create sequence log_tab_id
minvalue 1
maxvalue 999999999
start with 1
increment by 1
/
(1) 創(chuàng)建行級觸發(fā)器
create or replace trigger productinfo_oper_tgr
--當productinfo表insert之前觸發(fā)
before insert on productinfo
for each row
begin
if inserting then
insert into log_tab values(log_tab_id.nextval,'productinfo','insert',:new.productid,sysdate);
end if;
end;
/
行級觸發(fā)器里使用:new或:old來訪問變更前和變更后的數(shù)據(jù)。其中:
- 如果增加的是新紀錄操作,則只有
:new可以訪問。 - 如果是修改操作,則
:new或:old都可以訪問,:new表示修改后的數(shù)據(jù),:old表示修改前的數(shù)據(jù)。 - 如果是刪除操作,只有
:old可以訪問,因為該操作是刪除已有的記錄。
驗證觸發(fā)器:
insert into productinfo values('10','榮耀pad',2000,'平板',100,'鄭州','');
會發(fā)現(xiàn),productinfo插入一條數(shù)據(jù)后,log_tab表中也會插入一條記錄。
(2) 多種觸發(fā)事件
create trigger productinfo_oper_dml_tgr
after insert or update or delete
on productinfo
for each row
begin
case
--增加操作
when inserting then
insert into log_tab values(log_tab_id.nextval,'productinfo','insert',:new.productid,sysdate);
dbms_output.put_line('插入數(shù)據(jù)完成,主鍵是:' || :new.productid);
--更新操作
when updating then
insert into log_tab values(log_tab_id.nextval,'productinfo','update',:old.productid,sysdate);
dbms_output.put_line('更新數(shù)據(jù)完成,主鍵是:' || :old.productid);
--刪除操作
when deleting then
insert into log_tab values(log_tab_id.nextval,'productinfo','delete',:old.productid,sysdate);
dbms_output.put_line('刪除數(shù)據(jù)完成,主鍵是:' || :old.productid);
end case;
end;
/
驗證觸發(fā)器:
-
增加一條數(shù)據(jù):
SQL> insert into productinfo values('11','榮耀10',2400,'手機',80,'臺灣',''); 插入數(shù)據(jù)完成,主鍵是:11 已創(chuàng)建 1 行。 -
修改數(shù)據(jù):
SQL> update productinfo set DESCRIPTION = 'test' where productid in (7,8); 更新數(shù)據(jù)完成,主鍵是:7 更新數(shù)據(jù)完成,主鍵是:8 已更新2行。 -
刪除數(shù)據(jù):
SQL> delete from productinfo where productid = '11'; 刪除數(shù)據(jù)完成,主鍵是:11
(3) 在觸發(fā)器中使用if
如果修改的日期是25日,并且修改產(chǎn)品的價格高于3000,那么修改將終止。
create or replace trigger productinfo_oper_chk_tgr
before update of productprice on productinfo
for each row
begin
if (to_char(sysdate,'dd') = 25 and :old.productprice > 3000) then
raise_application_error(-20000,'今天是25日,不能修改價格高于3000的數(shù)據(jù)!');
end if;
insert into log_tab values (log_tab_id.nextval,'productinfo','insert',:new.productid,sysdate);
dbms_output.put_line('修改數(shù)據(jù)完成,主鍵是:' || :new.productid);
end;
/
(4) 使用when限制條件
當在表 productinfo 中增加的數(shù)據(jù)是”顯示器“類型時,需要把當前價格打九折
create or replace trigger productinfo_when_oper_tgr
before insert on productinfo
for each row
when (new.category = '顯示器')
begin
dbms_output.put_line('原價格:' || :new.productprice);
:new.productprice := :new.productprice * 0.9;
dbms_output.put_line('現(xiàn)價格:' || :new.productprice);
end;
/
驗證觸發(fā)器:
SQL> insert into productinfo values('11','榮耀顯示器',4599,'顯示器',90,'鄭州','無');
原價格:4599
現(xiàn)價格:4139.1
插入數(shù)據(jù)完成,主鍵是:11
已創(chuàng)建 1 行。
SQL> insert into productinfo values('12','華為matepad',4599,'平板',10,'鄭州','無');
插入數(shù)據(jù)完成,主鍵是:12
已創(chuàng)建 1 行。
2.4 DDL類型觸發(fā)器
所謂DDL類型觸發(fā)器,就是因DDL操作而激發(fā)的觸發(fā)器,主要包括CREATE、ALTER、 DROP等事件
create or replace trigger ddl_tgr
before create or alter or drop or rename on schema
begin
if sysevent = 'CREATE' then
dbms_output.put_line(dictionary_obj_name || '創(chuàng)建中...');
elsif sysevent = 'DROP' then
if dictionary_obj_name = 'TEST' then
raise_application_error(-20000,'不允許刪除Test表');
end if;
elsif sysevent = 'ALTER' then
raise_application_error(-20000,'不允許修改表');
elsif sysevent = 'RENAME' then
raise_application_error(-20000,'不允許修改表名');
end if;
end;
/
驗證觸發(fā)器:
SQL> create table test(
2 id number
3 );
TEST創(chuàng)建中...
表已創(chuàng)建。
SQL> rename test to test;
rename test to test
*
第 1 行出現(xiàn)錯誤:
ORA-00604: 遞歸 SQL 級別 1 出現(xiàn)錯誤
ORA-20000: 不允許修改表名
ORA-06512: 在 line 11
SQL> drop table test;
drop table test
*
第 1 行出現(xiàn)錯誤:
ORA-00604: 遞歸 SQL 級別 1 出現(xiàn)錯誤
ORA-20000: 不允許刪除Test表
ORA-06512: 在 line 6
常用事件屬性:
| 屬性函數(shù) | 可用的事件 | 簡介 |
|---|---|---|
| sysevent | 所有事件 | 返回激發(fā)觸發(fā)器的事件名稱 |
| instance_num | 所有事件 | 返回當前數(shù)據(jù)庫的實例號 |
| database_name | 所有事件 | 返回當前的數(shù)據(jù)庫名字 |
| server_error | servererror | 錯誤堆棧的置定位置返回錯誤號 |
| login_user | 所有事件 | 返回激發(fā)觸發(fā)器的用戶名 |
| dictionary_obj_type | create、alter、drop | 返回激活觸發(fā)器的ddl操作的對象類型 |
| dictionary_obj_name | create、alter、drop | 返回激活觸發(fā)器的ddl操作的對象名字 |
2.5 用戶和系統(tǒng)事件觸發(fā)器
所謂系統(tǒng)事件觸發(fā)器,就是基于Oracle系統(tǒng)事件而建立的觸發(fā)器。
該類型的觸發(fā)器可以審計數(shù)據(jù)庫的登錄、注銷以及關(guān)閉和啟動等。
??:該示例將記錄每個登錄用戶的時間,并把登錄時間存放到用戶登錄記錄表中。
具體步驟如下:
1)創(chuàng)建用戶登錄日志表
CREATE TABLE LOG_USER (
LOGONID VARCHAR2(50),
LOGONNAME VARCHAR2(50),
LOGONTIME DATE,
CONSTRAINT LOG_USER_PRK PRIMARY KEY(LOGONID)
);
2)創(chuàng)建觸發(fā)器。該觸發(fā)器是數(shù)據(jù)庫級,記錄每個用戶的登錄時間。具體腳本如下:
CREATE TRIGGER LOGOH_TGR
AFTER LOGON
ON DATABASE
BEGIN
INSERT IOTO LOG_USER
VALUES(LOG_TAB_ID.NEXTVXL,SYS.LOGIN_PSBR,SYSDATB);
END;
/
2.6 設(shè)置觸發(fā)器是否可用
觸發(fā)器被創(chuàng)建后,會不斷地被激發(fā),如果業(yè)務(wù)上不需要使用該觸發(fā)器了,則可以設(shè)置其是 否可用屬性,而不必把它刪除。
利用 ENABLE | DISABLE關(guān)鍵詞設(shè)置該觸發(fā)器是否可用。設(shè)置語法如下:
ALTBR TRIGGER [schena.]trigger DISABLE | ENABLE;
??:設(shè)置DDL_TGR觸發(fā)器不可用
alter trigger DDL_TGR disable;
2.7 查看觸發(fā)器狀態(tài)
select trigger_name,trigger_type,status from user_triggers;
其中 status為ENABLED表示當前觸發(fā)器啟用狀態(tài)。DISABLED表示當前觸發(fā)器禁用狀態(tài)。
3. 修改觸發(fā)器
修改觸發(fā)器同樣使用 replace 關(guān)鍵字。
在創(chuàng)建觸發(fā)器時帶上or replace關(guān)鍵字,從而完成觸發(fā)器的修改,也就是覆蓋。
4. 刪除觸發(fā)器
語法:
drop trigger [schema.]trigger_name;
??:刪除觸發(fā)器 DDL_TGR
drop trigger DDL_TGR;