1.什么是触发器?
触发器就是一个特殊的存储过程,当执行某些特定事件时(如DML操作,DDL操作,数据库事件),激活代码自动执行。
(个人理解,就像是前端页面的按钮一样, 给按钮添加一个点击事件,当点击按钮时自动执行JavaScript代码)
注意: 1.触发器不能包含事务控制语句,因为触发器是触发语句的一部分,触发语句被提交回退时,触发器也被提交回退了。
2.触发器中执行的操作不能为DDL操作(不能在激活触发器时,create、drop、alter)
3.触发器内的代码有大小限制,最大为32KB,如果要执行的代码文件过大时,可以将代码放入存储过程中,然后通过触发器调用存储过程
4.触发器不接受参数
5.一个表最多有12个触发器,但同一时间,同一事件,同一类型的触发器只能有一个。各触发器之间不能有矛盾。一个表上的触发器越多,对该表的DML操作影响越大
2.触发器的分类:
DML触发器:DML操作时触发的。
替代触发器:针对视图的。
数据库事件触发器:执行数据库系统操作时激活。
3.触发器的构成
触发事件:当执行什么事件时激活触发器,如执行update、delete操作时
触发时间:在执行事件之前激活触发器还是执行事件之后激活触发器
触发对象:操作哪儿张表时激活触发器(触发器创建后存放在表中,相当于定义触发器针对的表对象)
触发操作:当激活触发器时,要执行的操作,如(DML操作或报异常)
4.创建触发器:
-- 创建触发器 create or replace trigger 触发器名 before|after|instead of 触发时间 on 触发对象 表|视图 (for each row) begin 触发操作 end;
-- 创建一个简单的触发器,不允许修改表数据 create or replace trigger tg1 --当有多个操作时,用or连接,如before update or insert or delete -- 使用of关键字可以定位到列,也可以不写,表示整张表 before update on emp begin -- 触发器执行的代码块, raise_application_error(-20002, '不允许修改表数据');--定义一个异常,当触发器激活时,发生异常,操作失败回滚 end; -- 执行修改语句测试 update emp set sal=100 where DEPTNO=10;
-- 创建一个触发器,当对emp表执行增改操作时,对工资列设置触发器 create or replace trigger tg1 -- 使用of关键字,定位到列 before insert or update of sal -- for each row 关键字,如果不写,表示是表级触发器,写了就表示是行级触发器 on EMP for each row begin -- inserting |updating |deleting 判断当前操作是哪种操作 if inserting then--录入操作 if :new.sal is null then raise_application_error(-20390,'工资不能为空'); end if; elsif updating then -- 使用 :old|:new 表示旧的数据及新的数据,下面判断语句意思为,如果原来的工资大于修改后的工资,抛异常 -- :old|:new 操作不能在表级触发器中 if :old.sal>:new.SAL then raise_application_error(-20389,'不能降工资'); end if; end if; end; --测试触发器 insert into emp(empno,ename) values(8003,'张三'); update emp set sal=500 where empno=8001;
for each row:表示当前的触发器为行级触发器,行级触发器与表级触发器的区别是:行级触发器要求,当操作语句影响多行时,要求每一行的数据,只要符合约束条件,均激活一次触发器;而表级触发器,只要操作的语句满足约束条件,只激活一次触发器。
-- 行级触发器及表级触发器的区别: -- 创建行级触发器 create or replace trigger tg2 before update on emp for each row begin dbms_output.put_line('修改了数据'); end; -- 测试,测试结果为部门20有多少员工,就输出多少遍‘修改了数据’ update emp set comm=nvl(comm,0)+2000 where DEPTNO=20; -- 创建表级触发器,为了防止触发器间相互影响,因此都用同一个名字,使用or replace来替换 create or replace trigger tg2 before update on emp begin dbms_output.put_line('修改了数据'); end; -- 再次测试,测试结果为不论部门20有多少员工,只输出一次‘修改了数据’,即触发器只激活了一次 update emp set comm=nvl(comm,0)+2000 where DEPTNO=20;
:old 行数据,表示旧的数据(update、delete之前)
:new 行数据, 表示新的数据(insert、update之后)
after触发器
-- 创建员工表的历史表,执行删除操作后,将删除的数据存入历史表中 create table emp_his as select * from emp where 1=2; select * from emp_his; -- 创建触发器,执行删除操作后,将删除的数据存入历史表 create or replace trigger tg3 after delete on emp for each row begin -- 将数据存入历史表 insert into emp_his(empno, ename, job, mgr, hiredate, sal, comm, deptno) values (:old.EMPNO, :old.ENAME, :old.JOB,:old.MGR,:old.HIREDATE,:old.SAL,:old.COMM, :old.DEPTNO); end; -- 测试触发器 delete from EMP where empno=8001; select * from emp_his;
替代触发器
替代触发器通常与视图一起使用,当我们在查询数据时,有时需要联合多张表一块查询,这样每次查询都要重新编写语句将表联合起来,那么能不能将联表后查询到的数据放入同一个表格中,方便查看呢?视图就是这样的一个表,将查询到的数据存入视图中,这样我们需要查询时,直接查询视图就可以了,避免了多次联表查询。
因为视图是多张表的联合数据,因此只能进行查询操作,不能进行添加,修改,删除等, 因为这些数据都是不同的表中的数据,要操作的话,要进入不同的表中进行操作,要想在视图中进行增删改等操作,就要用到替代触发器
-- 首先使用管理员登录,给用户scott分配创建视图的权限 grant create view to scott; -- 创建视图 create or replace view view1 as select empno, ename, EMP.DEPTNO, dname from emp,dept where emp.DEPTNO = DEPT.DEPTNO; select * from view1; insert into view1 values(8001,'zhangsan',50,'人事部'); -- 创建触发器 create or replace trigger tg4 instead of insert on view1 for each row begin insert into dept(deptno, dname)values (:new.DEPTNO,:new.dname); insert into EMP(empno, ename, deptno) values (:new.empno,:new.ename,:new.DEPTNO); end; -- 关闭之前创建的触发器,避免对该触发器产生的影响 ALTER TRIGGER tg1 disable ; insert into view1 values(8001,'zhangsan',50,'人事部');
数据库事件触发器:
执行相应的数据库事件时会激活触发器
-- 模拟创建实验表,数据库触发器创建需要管理员权限 create table login_user_table( username varchar2(20), ip varchar2(20) ); create or replace trigger tg4 after logon on database begin insert into login_user_table values (ora_login_user, ora_client_ip_address); end; -- 切换不同的用户登录 select * from login_user_table;