复习——用check短语满足用户的要求
例子:创建Student2表,要求Ssex只允许取“男”或“女”。
CREATE TABLE Student2
(Sno varchar2(9) PRIMARY KEY,
Sname varchar2(8) NOT NULL,
Ssex varchar2(2) CHECK (Ssex IN (‘男’,‘女’) ) ,
Sage SMALLINT,
Sdept varchar2(20)
);
一、DML触发器(触发器的触发事件为DML语句insert or update or delete)
1.创建一个触发器,当XS表中记录被删除时,请备份下删除的记录,方式:写到新建表XS_1中,以备查看。
//创建表 xs_1,搭建平台(只保留学生表的结构)
create table xs_1 as select * from xs;
truncate table xs_1;
//创建触发器
create trigger t1
before delete on xs
for each row
declare
begin
insert into xs_1(xh,xm,zym,xb,cssj,zxf,bz) values(:old.xh,:old.xm,:old.zym,:old.xb,:old.cssj,:old.zxf,:old.bz);
end t1;
//调用
delete from xs where xh='001';
select * from xs;
select * from xs_1;
2.监控用户对XS表的操作,要求:当XS表执行插入、更新和删除3种操作后在sql_info表中给出相应提示和执行时间。
//创建sql_info表
create table sql_info(info varchar(10),time date);
create or replace trigger t2
after update or delete or insert on xs
for each row
declare
v_info sql_info.info%type;
begin
if inserting then v_info:='插入';
elsif updating then v_info:='更新';
else v_info:='删除';
end if;
insert into sql_info values(v_info,sysdate);
end t2;
delete from xs where xh='101112'; //调用
3.当插入新员工时显示新员工的员工号、员工名;当更新员工工资时,显示修改前后员工工资;当删除员工时,显示被删除的员工号、员工名。
create or replace trigger t3
after update or insert or delete on scott.emp
for each row
begin
if inserting then dbms_output.put_line(:new.ename||' '||:new.empno); //新员工号表示方法
elsif updating then dbms_output.put_line(:old.sal||' '||:new.sal);
else dbms_output.put_line(:old.empno||' '||:old.ename);
end if;
end t3;
Set serveroutput on
declare
begin
update scott.emp set sal=7777 where empno=7788;
commit;
end;
4.此例为语句级触发器
针对Scott.emp表,记录其相应操作的信息,具体如下:
当执行插入操作时,统计操作后员工人数;
当执行更新工资操作时,统计更新后员工平均工资;
当执行删除操作时,统计删除后各个部门剩余的人数(游标)。
create or replace trigger t4
after update or insert or delete on scott.emp
declare
v1 number;
v2 scott.emp.sal%type;
begin
if inserting then
select count(*) into v1 from scott.emp;
dbms_output.put_line('添加记录后总人数为'||v1);
elsif updating then
select avg(sal) into v2 from scott.emp;
dbms_output.put_line('更新记录后平均工资为'||' '||v2);
else
for v_s in (select deptno,count(*) num from scott.emp group by deptno)
loop
dbms_output.put_line('删除记录后各个部门的部门号和人数为' ||v_s.deptno||' '||v_s.num);
end loop;
end if;
end t4;
delete from scott.emp where hiredate<=to_date('1980-12-17','yyyy-mm-dd');
注意:
对于oracle行级触发器(for each row),不能对本表做任何操作,即行级触发器中,不能查询和修改(DML)自身表 。否则会触发 ORA-04091 关于在oracle行级触发器中访问本表的错误。
二、系统触发器(系统触发器在发生如数据库启动或关闭等系统事件时触发)
功能要求:通过触发器记录是何用户,何时登陆了系统。
//先创建用户活动表
create table u1
(username varchar2(10),
activity varchar2(10),
time date);
//创建登录触发器
create or replace trigger t5
after logon on database
begin
insert into u1 values(user,'LOGON',sysdate);
end t5;
//调用
select username,activity,to_char(time,'yyyy-mm-dd hh24:mi') from u1;
通过触发器记录是何用户,何时退出了系统。
用户表和上面的一样即可
create or replace trigger t6
before logoff on database
begin
insert into u1 values(user,'退出',sysdate);
end t6;
select username,activity,to_char(time,'yyyy-mm-dd hh24:mi') from u1;
三、触发器提升
(1)通过触发器禁止用户的某项操作
功能要求:建一触发器,作用为禁止在星期四改变scott.emp雇员信息(包括添加删除和修改)。
预备知识:
①日期型转化为字符串型
Select to_char(sysdate,'yyyy-MM-dd HH24:mi') from dual;
②通常用户自定义异常是在声明后才能产生,但raise_application_error函数就可以直接产生异常。
语法: raise_application_error(错误号,错误信息);
错误号和值在-20000到-20999之间,错误信息的文本长度最大不能超过512个字
函数作用:将应用程序专有的错误从服务器端转达到客户端应用程序,并禁止用户的该项操作
create or replace trigger t7
before insert or update or delete on scott.emp
begin
if to_char(sysdate, 'DAY') in ('星期四') then
raise_application_error(-20001,'不能在星期四修改员工信息');
end if;
end t7;
update scott.emp set ename='candy' where empno=7876;
(2)注意: 权限问题和变异表问题
创建一个触发器,在修改dept表的部门号后,同时更新emp表中相应的员工的部门号。
(主键动,外键从。修改dept表的部门号后,emp表的员工部门号会自动更改)
create or replace trigger tr
after update of deptno on scott.dept
for each row
begin
update scott.emp set deptno=:new.deptno
where deptno=:old.deptno;
end tr;
注意:必须先删掉system用户下所有的trigger,然后切换到Scott用户创建触发器才可以。
(3) 补充---变异表的解决方案
Oracle变异表ORA-04091错误的解决方案
针对scott.emp表为了实现在更新员工所在部门时,该部门中员工人数不超过8人,请建立一个触发器。
(为了实现在更新员工所在部门或向部门插入新员工时,部门中员工人数不超过8人,可以在emp表上创建两个触发器,同时创建一个共享信息的包。 )
解决方案:
n一个package,用来共享变量
n一个for each row 触发器,用来给共享变量赋值
n一个语句级触发器,用来做计算或者统计。
//创建包
create or replace package pp
as
v_deptno scott.dept.deptno%type;
end pp ;
//触发器1
create or replace trigger t1
before insert or update of deptno on scott.emp for each row
begin
pp.v_deptno:=:new.deptno;
end t1;
//触发器2
create or replace trigger t2
after insert or update of deptno on scott.emp
declare
v_num number(3);
begin
select count(*) into v_num from scott.emp
where deptno=pp.v_deptno;
if v_num>2 then
raise_application_error(-20003,'too many employees in department'||pp.v_deptno);
end if;
end t2;
//调用
insert into scott.emp(empno,ename,sal,deptno) values(2,'WANG',2000,10);