触发器是一种特殊类型的存储过程,类似于其他编程语言的事件函数,当有操作影响到触发器管理的数据时。触发器就会自动发生。触发器也是保护数据完整性的一种重要方法,于存储过程不同的是,触发器是通过事件进行触发被执行,存储过程需要被调用执行。oracle提供5种类型的触发器:语句触发器,行触发器,INSTEAD OF触发器,系统条件触发器和用户事件触发器。BEFORE:语句执行前触发;AFTER:语句执行后触发
语句触发器是在表上或则某些情况下的视图上执行的特定语句或则语句组上的触发器,于INSERT,UPDATE,DELETE或则组合上进行关联。但是无论使用什么样的组合,各个语句触发器都只会针对指定语句激活一次。
创建语句触发器:
create or replace trigger tri_1
before insert or delete or update on stu
begin
if inserting then
dbms_output.put_line('emp数据表有数据inserting...');
end if;
if deleting then
dbms_output.put_line('emp数据表有数据deleting...');
end if;
if updating then
dbms_output.put_line('emp数据表有数据updating...');
end if;
end;
行触发器是指为受影响的各个行激活触发器,即每影响到一行记录就触发一次。行触发器的定义语句与语句触发器类似,只需要加上for each row参数。
创建行触发器:
drop trigger tri_2;
create or replace trigger tri_2
after insert or delete or update on stu
for each row
begin
if inserting then
dbms_output.put_line('inserting...');
end if;
if deleting then
dbms_output.put_line('deleting...');
end if;
if updating then
dbms_output.put_line('updating...');
end if;
end;
测试如图:
注意:对于表同时创建行触发器和语句触发器时会被同样触发,先触发语句触发器时再触发行触发器。
使用updete of columnname on tablename创建指定列触发器:
create or replace trigger tri_3
after update of no on stu
for each row
begin
if updating then
raise_application_error(-20001,'不能修改emp.no列的数据');
end if;
end;
INSTEAD OF触发器是针对视图对象的触发器;用于对视图插入数据时实现对基于视图基本表的数据插入。
创建INSTEAD OF触发器:
create or replace trigger tri_4
instead of insert on grade_v
for each row
begin
insert into STU(NO,NAME,GENTLE,AGE,DEPT)
values(:new.NO,:new.NAME,:new.GENTLE,:new.AGE,:new.DEPT);
insert into GRADE(NO,NAME,SCORE)
values(:new.NO,:new.GRADENAME,:new.SCORE);
end;
测试如图:
使用INSTEAD OF注意事项:
1.只能被创建在视图上,并却该视图没有指定WITH CHECK OPTION选项。
2.不呢个指定BEFORE或AFTER。
3.FOR EACH ROW子句是可选的,即INSTEAD OF触发器只能在行级上触发,或只能是行级触发器,没必要指定。
用户事件触发器是在用户事件上触发的,一般包括:用户登录,注销,修改结构等,可以在CREATE,ALTER,DROP等DLL操作或数据库系统上被触发。
create or replace trigger tri_5
after ddl on schema
begin
dbms_output.put_line('执行了ddl语句');
end;
系统事件触发器是在系统事件上触发的触发器,系统事件一般包括数据库启动,关闭,用户的登录与退出,服务器错误等事件,其可以在对数据库的STARTUP,SHUTDOWN及用户的LOGON,LOGOFF等操作时被触发
创建系统事件触发器:
create table logon_event(
user_name varchar2(20),
address varchar2(20),
logon_date timestamp,
logoff_date timestamp
);
create or replace trigger tri_6
after logon on database
begin
insert into logon_event(user_name,address,logon_date)
values(ora_login_user,ora_client_ip_address,systimestamp);
end;
创建记录日志触发器及相关对象
--创建日志表
CREATE TABLE stu_log(
LOGID NUMBER,
LOGACTION VARCHAR2(1),
NO VARCHAR2(20),
NAME VARCHAR2(20),
GENTLE VARCHAR2(4),
AGE NUMBER(2),
DEPT VARCHAR2(20),
LOGTIME DATE
);
--创建序列号
DROP SEQUENCE stu_seq;
CREATE SEQUENCE stu_seq
START WITH 1
MAXVALUE 999999999999999999999999999
MINVALUE 1
NOCYCLE
NOCACHE
NOORDER;
--创建记录日志触发器
CREATE OR REPLACE TRIGGER tri_7
AFTER INSERT OR DELETE OR UPDATE ON stu
REFERENCING NEW AS NEW OLD AS OLD--BEGIN...END中使用别名来引用
FOR EACH ROW--FOR EACH ROW表示是行级触发器,每操作成功一行就会触发一次
DECLARE
currseq stu_log.LOGID%TYPE;
BEGIN
SELECT stu_seq.CURRVAL INTO currseq FROM DUAL;
IF INSERTING THEN
INSERT INTO stu_log(LOGID,
LOGACTION,
NO,
NAME,
GENTLE,
AGE,
DEPT,
LOGTIME)
VALUES (currseq,
'I',
:NEW.NO,
:NEW.NAME,
:NEW.GENTLE,
:NEW.AGE,
:NEW.DEPT,
(SELECT SYSDATE FROM DUAL));
END IF;
IF DELETING THEN
DELETE FROM stu_log WHERE LOGID = currseq AND LOGACTION IN ('O', 'U');
INSERT INTO stu_log(LOGID,
LOGACTION,
NO,
NAME,
GENTLE,
AGE,
DEPT,
LOGTIME)
VALUES (currseq,
'D',
:OLD.NO,
:OLD.NAME,
:OLD.GENTLE,
:OLD.AGE,
:OLD.DEPT,
(SELECT SYSDATE FROM DUAL));
END IF;
IF UPDATING THEN
INSERT INTO stu_log(LOGID,
LOGACTION,
NO,
NAME,
GENTLE,
AGE,
DEPT,
LOGTIME)
VALUES (currseq,
'U',
:NEW.NO,
:NEW.NAME,
:NEW.GENTLE,
:NEW.AGE,
:NEW.DEPT,
(SELECT SYSDATE FROM DUAL));
INSERT INTO stu_log(LOGID,
LOGACTION,
NO,
NAME,
GENTLE,
AGE,
DEPT,
LOGTIME)
VALUES (currseq,
'O',
:OLD.NO,
:OLD.NAME,
:OLD.GENTLE,
:OLD.AGE,
:OLD.DEPT,
(SELECT SYSDATE FROM DUAL));
END IF;
END;
--查看触发器
select * from user_triggers where table_name = 'STU';--要大写!!!
--禁用/启动触发器
alter trigger tri_1 disable;
alter trigger tri_1 enable;
--以表为单位禁用/启动触发器
alter table stu disable all triggers;
alter table stu enable all triggers;
--删除触发器
--drop trigger tri_1;
修改触发器关联表时需要先drop trigger;否则会遇到如下情况:
DROP TABLE STU CASCADE CONSTRAINTS;
CREATE TABLE STU
(
NO VARCHAR2(20 BYTE),
NAME VARCHAR2(20 BYTE),
GENTLE VARCHAR2(4 BYTE),
AGE NUMBER(2),
DEPT VARCHAR2(20 BYTE)
);
SET DEFINE OFF;
Insert into STU
(NO, NAME, GENTLE, AGE, DEPT)
Values
('120006', '李飒', '男', 12, '12工商管理');
Insert into STU
(NO, NAME, GENTLE, AGE, DEPT)
Values
('120005', '林琳', '女', 22, '12计算机');
Insert into STU
(NO, NAME, GENTLE, AGE, DEPT)
Values
('120004', '杨过', '男', 22, '12计算机');
Insert into STU
(NO, NAME, GENTLE, AGE, DEPT)
Values
('120003', '张清', '女', 21, '12外语');
Insert into STU
(NO, NAME, GENTLE, AGE, DEPT)
Values
('120001', '陈诚', '男', 23, '12计算机');
Insert into STU
(NO, NAME, GENTLE, AGE, DEPT)
Values
('120002', '李宗赫', '男', 25, '12图形');
Insert into STU
(NO, NAME, GENTLE, AGE, DEPT)
Values
('120007', '李飒11', '男', 12, '12工商管理');
COMMIT;
------------------------
DROP TABLE GRADE CASCADE CONSTRAINTS;
CREATE TABLE GRADE
(
NO VARCHAR2(10 BYTE) NOT NULL,
NAME VARCHAR2(20 BYTE),
SCORE NUMBER
);
SET DEFINE OFF;
Insert into GRADE
(NO, NAME, SCORE)
Values
('120001', '计算机基础', 85);
Insert into GRADE
(NO, NAME, SCORE)
Values
('120003', '计算机基础', 96);
Insert into GRADE
(NO, NAME, SCORE)
Values
('120004', '计算机基础', 60);
COMMIT;
---------------------
DROP VIEW GRADE_V;
CREATE OR REPLACE FORCE VIEW GRADE_V
(
NO,
NAME,
GENTLE,
AGE,
DEPT,
GRADENAME,
SCORE
)
AS
SELECT STU.NO AS NO,
STU.NAME AS NAME,
STU.GENTLE AS GENTLE,
STU.AGE AS AGE,
STU.DEPT AS DEPT,
GRADE.NAME AS GRADENAME,
GRADE.SCORE AS SCORE
FROM STU, GRADE
WHERE STU.NO = GRADE.NO;