一、触发器(trigger)
推荐练习文档:
http://wenku.baidu.com/link?url=brtd9myO4XIaEjRPaRB0yWgV2_TfXmNxS6KKjB_avq7d0hPs3SknJdrs-JLtWe–H-f3q-I61HUxkcqqAFMC0z6Zf65QBbmFRB8TAlULmGS
http://www.cnblogs.com/friends-wf/p/3793417.html存储过程练习
http://blog.csdn.net/indexman/article/details/8023740/(推荐博客触发器)(强烈推荐!!!)
触发器会被编译且存储在数据库服务器中。由一个事件来启动运行,即触发器是当某个事件发生时自动隐式运行。并且触发器不能接收参数。所以运行触发器就叫触发或点火。oracle事件指的是对数据库的表进行insert、update及delete操作或对视图进行类似的操作。oracle将触发器的功能扩展到了触发oracle,如数据库的启动和关闭等。所以触发器常用来完成由数据库的完整性约束难以完成的复杂业务规则的约束,或用来监视对数据库的各种操作,实现审计功能。
1、创建触发器
create [or replace] trigger 触发器名
before|after|instead of 某个事件 on 表名|视图名|database|用户名.schema
[for each row [when]]
PL/SQL块|调用过程(call procedure_name)or replace:如果数据库已经存在同名的触发器,则替换
before|after:指定触发时间点
instead of:替换视图操作,只能为行级触发器
某个事件:指定当发生什么操作的时候,触发触发器
on:指定基于什么创建触发器
for each row [when]:表示改变每行数据时,执行一次触发器
PL/SQL块|调用过程(call procedure_name):触发器执行时,执行的代码
2、触发器分类:
关于触发器先前介绍:
before触发器:在触发语句运行前先运行触发动作
create [or replace] trigger 触发器名
before insert|delete|update on 表名
before create|drop|alter on database|用户名.schema
[for each row [when]]
PL/SQL块|调用过程(call procedure_name)
行级触发器:当某触发事件发生时,对受到该操作影响的每一行数据,触发器都单独执行一次。指定了for each row选项。
create [or replace] trigger 触发器名
before|after|instead of insert|delete|update on 表名|视图名
for each row [when]
PL/SQL块|调用过程(call procedure_name)
在行级触发器的PL/SQL块和when子句中可以使用相关名称参照当前的新、旧列值,默认的相关名称分别为new、old。触发器的PL/SQL块中应用相关名称时,必须在它们之前加冒号(:),但在when子句中则不加冒号。
触发器在数据库里以独立的对象存储,它与存储过程和函数不同的是,存储过程与函数需要用户显示调用才执行,而触发器是由一个事件来启动运行。即触发器是当某个事件发生时自动地隐式运行。并且,触发器不能接收参数。所以运行触发器就叫触发或点火(firing)。ORACLE事件指的是对数据库的表进行的INSERT、UPDATE及DELETE操作或对视图进行类似的操作。ORACLE将触发器的功能扩展到了触发ORACLE,如数据库的启动与关闭等。所以触发器常用来完成由数据库的完整性约束难以完成的复杂业务规则的约束,或用来监视对数据库的各种操作,实现审计的功能。
(1) 替代触发器
由于在ORACLE里,不能直接对由两个以上的表建立的视图进行操作。所以给出了替代触发器。它就是ORACLE 8专门为进行视图操作的一种处理方法。
(2) 系统触发器
ORACLE 8i 提供了第三种类型的触发器叫系统触发器。它可以在ORACLE数据库系统的事件中进行触发,如ORACLE系统的启动与关闭等。
(3)语句级触发器
当某触发事件发生时,该触发器只执行一次。未指定for each row选项。
create [or replace] trigger 触发器名
before|after insert|delete|update on 表名|视图名
before|after create|drop|alter on database|用户名.schema
PL/SQL块|调用过程(call procedure_name)
其中行级触发器对DML语句影响的每个行执行一次.语句级触发器对每个DML语句执行一次,如果在TABLE表中插入的数据为500行,那么这个表上的语句级触发器只执行一次,而行级的触发器就要执行500次了。 是指为受到影响的各个行激活的触发器,定义与语句级触发器类似,但是要注意下面两个不同点:
1、 定义语句中包含FOR EACH ROW子句
2、 在BEFORE……FOR EACH ROW触发器中,用户可以引用受到影响的行值。
(4) DML触发器
ORACLE可以在DML语句进行触发,可以在DML操作前或操作后进行触发,并且可以对每个行或语句操作上进行触发。
(4.1)创建DML触发器
触发器名与过程名和包的名字不一样,它是单独的名字空间,因而触发器名可以和表或过程有相同的名字,但在一个模式中触发器名不能相同。
(4.2)DML触发器的限制
CREATE TRIGGER语句文本的字符长度不能超过32KB;
触发器体内的SELECT 语句只能为SELECT … INTO …结构,或者为定义游标所使用的SELECT 语句。
触发器中不能使用数据库事务控制语句 COMMIT; ROLLBACK, SVAEPOINT 语句;
由触发器所调用的过程或函数也不能使用数据库事务控制语句;
触发器中不能使用LONG, LONG RAW 类型;
触发器内可以参照LOB 类型列的列值,但不能通过 :NEW 修改LOB列中的数据;
(4.3)DML触发器基本要点
触发时机:指定触发器的触发时间。如果指定为BEFORE,则表示在执行DML操作之前触发,以便防止某些错误操作发生或实现某些业务规则;如果指定为AFTER,则表示在执行DML操作之后触发,以便记录该操作或做某些事后处理。
触发事件:引起触发器被触发的事件,即DML操作(INSERT、UPDATE、DELETE)。既可以是单个触发事件,也可以是多个触发事件的组合(只能使用OR逻辑组合,不能使用AND逻辑组合)。
条件谓词:当在触发器中包含多个触发事件(INSERT、UPDATE、DELETE)的组合时,为了分别针对不同的事件进行不同的处理,需要使用ORACLE提供的如下条件谓词。
1)INSERTING:当触发事件是INSERT时,取值为TRUE,否则为FALSE。
2)UPDATING [(column_1,column_2,…,column_x)]:当触发事件是UPDATE 时,如果修改了column_x列,则取值为TRUE,否则为FALSE。其中column_x是可选的。
3)DELETING:当触发事件是DELETE时,则取值为TRUE,否则为FALSE。
解发对象:指定触发器是创建在哪个表、视图上。
触发类型:是语句级还是行级触发器。
触发条件:由WHEN子句指定一个逻辑表达式,只允许在行级触发器上指定触发条件,指定UPDATING后面的列的列表。
(5)instead of 触发器
用于对视图的DML触发,由于视图有可能是多个表进行联接而成,因而并非所有视图都是可更新的。但可以按照所需的方式执行更新。
create [or replace] trigger 触发器名
instead of insert|delete|update on 视图名
[for each row [when]]
PL/SQL块|调用过程(call procedure_name)
只能创建在视图上,并且该视图没有指定with check option选项;
for each row子句是可选的,即instead of触发器只能在行级上触发、或只能是行级触发器,
没有必要指定;
没有必要在针对一个表的视图上创建instead of触发器,只要创建DML触发器就可以了。
3、触发器注意事项
编写触发器时,牢记触发器有一下几个部分组成:
触发事件:引起触发器被触发的事件。 例如:DML语句(INSERT, UPDATE,DELETE语句对表或视图执行数据处理操作)、DDL语句(如CREATE、ALTER、DROP语句在数据库中创建、修改、删除模式对象)、数据库系统事件(如系统启动或退出、异常错误)、用户事件(如登录或退出数据库)。
触发时间:即该TRIGGER 是在触发事件发生之前(BEFORE)还是之后(AFTER)触发,也就是触发事件和该TRIGGER 的操作顺序。
触发操作:即该TRIGGER 被触发之后的目的和意图,正是触发器本身要做的事情。 例如:PL/SQL 块。
触发对象:包括表、视图、模式、数据库。只有在这些对象上发生了符合触发条件的触发事件,才会执行触发操作。
触发条件:由WHEN子句指定一个逻辑表达式。只有当该表达式的值为TRUE时,遇到触发事件才会自动执行触发器,使其执行触发操作。
触发频率:说明触发器内定义的动作被执行的次数。即语句级(STATEMENT)触发器和行级(ROW)触发器。
语句级(STATEMENT)触发器:是指当某触发事件发生时,该触发器只执行一次;
行级(ROW)触发器:是指当某触发事件发生时,对受到该操作影响的每一行数据,触发器都单独执行一次。
编写触发器时,需要注意以下几点:
触发器不接受参数。
一个表上最多可有12个触发器,但同一时间、同一事件、同一类型的触发器只能有一个。并各触发器之间不能有矛盾。
在一个表上的触发器越多,对在该表上的DML操作的性能影响就越大。
触发器最大为32KB。若确实需要,可以先建立过程,然后在触发器中用CALL语句进行调用。
在触发器的执行部分只能用DML语句(SELECT、INSERT、UPDATE、DELETE),不能使用DDL语句(CREATE、ALTER、DROP)。
触发器中不能包含事务控制语句(COMMIT,ROLLBACK,SAVEPOINT)。因为触发器是触发语句的一部分,触发语句被提交、回退时,触发器也被提交、回退了。
在触发器主体中调用的任何过程、函数,都不能使用事务控制语句。
在触发器主体中不能申明任何Long和blob变量。新值new和旧值old也不能是表中的任何long和blob列。
不同类型的触发器(如DML触发器、INSTEAD OF触发器、系统触发器)的语法格式和作用有较大区别。
4、触发器代码
在scott用户下,创建三个触发器:
Sql代码
-- 语句级触发器
-- 禁止员工在休息日改变雇员信息
create or replace trigger tr_sec_emp
-- before:在DML事件之前执行
before insert or update or delete on emp
-- 如果在这里没有指定是行级还是语句级触发器,默认就是 FOR EACH STATEMENT
begin
if to_char(sysdate,'DAY','nls_date_language=AMERICAN')
in('SAT','SUN') then
case
-- 当触发事件是Insert操作时,该条件谓词返回值为True,否则为False
when inserting then
raise_application_error(-20001,'不能在休息日增加雇员信息!');
-- 当触发事件是Update操作时,该条件谓词返回值为True,否则为False
when updating then
raise_application_error(-20002,'不能在休息日修改雇员信息!');
-- 当触发事件是Delete操作时,该条件谓词返回值为True,否则为False
when deleting then
raise_application_error(-20003,'不能在休息日删除雇员信息!');
end case;
end if;
end;
/
Sql代码
-- 限制员工的工资不能超过当前的最高工资
create or replace trigger tr_emp_salary
-- update of 后面指定在那些列被改变时才调用这个触器,如果不加of 默认是所有列
before update of sal on emp
-- 指定该触发器为行级触发器
for each row
-- 如果要定义变量,则只能在Declare中定义
declare
maxSalary number(10,2);
begin
select max(sal) into maxSalary from emp;
if :new.sal > maxSalary then
raise_application_error(-20010,'员工工资超出工资上限!');
end if;
end;
/
Sql代码
-- 设置员工的工资不能低于原工资,但也不能高出原工资的20%
create or replace trigger tr_emp_say
before update of sal on emp
for each row
-- 设置执行触发器的条件
when (new.sal < old.sal or new.sal > old.sal*1.2)
begin
raise_application_error(-20011,'员工的工资不能降薪,但工资升幅不能超过20%!');
end;
/
对于上述触发器可以进行的一些操作:
Sql代码
-- 显示触发器
select trigger_name,status from user_triggers;
-- 禁止触发器
alter trigger tr_emp_salary disable;
-- 激活触发器
alter trigger tr_emp_salary enable;
-- 禁止表的所有触发器
alter table emp disable all triggers;
-- 激活表的所有触发器
alter table emp enable all triggers;
-- 重新编译触发器
--alter trigger tr_emp_salary compile;
-- 删除触发器
drop trigger tr_emp_salary;
二、存储PL/SQL单元
存储PL/SQL单元就是存储过程、函数、包,有名字,可以调用,会被存储到数据库服务器的PL/SQL代码,数据字典中;子程序可以接收参数;包子程序:属于某个包;独立子程序:不属于任何包;函数有返回值。
子程序参数有三种模式:
in:传入参数,只能用于传入值,不能修改,调用时必须传值,该模式是默认模式
out:传出参数,只能用于把值返回给调用者,不能用于接收值(接收不到值)
in out:以上两种的结合
三、存储过程
创建过程时,系统会对其进行编译,并将执行代码存储到数据库中。有名字,可接收参数,没有返回值。
存储过程定义语法:
create [or replace] procedure 过程名 [(参数名 参数模式 参数类型,...)] is|as
[declaration] --定义部分
begin
--执行部分
[exception]
--异常处理部分
end [过程名];
创建时不执行,调用时才执行。(1).在SQL>提示符下调用过程,execute 过程名(参数 …)(2).在PL/SQL块中调用过程,过程名(参数 …),可独立作为PL/SQL语句.可以在匿名块、其他的存储过程、函数中调用。存储过程中可以直接执行DML语句(select、insert、update、delete),不能直接执行DDL语句。存储过程中如需执行DDL语句,先把DDL语句存放在一个字符串变量中,然后通过execute immediate语句执行字符串内容,字符串内容必须符合SQL语法。用户从角色中获取的权限,在PL/SQL块中
存储过程中创建触发器实现主键自增长:
create or replace procedure pk_auto_increment_pro(t_name varchar2) is
v_ddl varchar2(2000);
begin
v_ddl := 'create or replace trigger pk_auto_increment_' || t_name ||
' before insert on ' || t_name ||
' for each row
begin
dbms_output.put_line(''赋值前id:'' ||:new.id);
select test_seq.nextval into :new.id from dual;
dbms_output.put_line(''赋值后id:'' ||:new.id);
end;';
execute immediate v_ddl;
end;
四、函数
函数的编译号存放在数据库服务器的代码,必须有返回值。
创建函数:
create [or replace] function 函数名 [(参数列表)] return 返回值类型 is|as
[local declarations] --定义部分
begin
--执行部分
return 结果; 一定要有retutn,用于返回值
exception
--异常处理部分
end;
两种方式调用函数:
1.在SQL>提示符下调用函数
var varName 类型; --定义变量
call 函数名 into :varName;
print varName;
2.在SQL语句、PL/SQL块中调用函数,不能作为独立的PL/SQL语句,参照表达式用法
(表达式不是语句,可以作为其他语句的一部分)。
五、包
包用于将相关的对象组合在一起,一个任务,需要多个函数或者过程协同才能完成,这时,可以这些函数和存储过程都放在一个包下,方便函数和存储过程的管理,提高程序的性能,在第一次用的时候,会全部读入内存,下次用直接从内存取,加强模块化。
包分为两部分:
1.包头(类似java接口)
包的规范,只包含了函数或存储过程的说明部分
创建包头语法:
create [or replace] package 包名 is|as
--函数定义
function 函数名 [(参数列表)] return 返回类型;
--存储过程定义
procedure 存储过程名 [(参数列表)];
end [包名];
2.包体(类似java实现类)
实现包头里定义的规范
创建包体语法:
create [or replace] package body 包名 is|as
--函数实现
function 函数名 [(参数列表)] return 返回类型 is|as
函数实现内容
--存储过程实现
procedure 存储过程名 [(参数列表)] is|as
存储过程实现内容
end [包名];
使用包子程序的时候,包名.子程序名。调用存储过程时,如果不需要传参,调用该过程的时候,可以加(),也可以不加。调用函数时,如果不需要传参,在SQL*Plus中运行,必须加(),其他不需要