Java相关课程系列笔记之三PLSQL学习笔记 下载本文

十一、包package

11.1什么是package

1)package(包)是一个可以将相关对象存储在一起的PL/SQL结构。package包含了两个分离的组成部分:specification(package的声明,即包声明)和body(声明中的程序实现,即包体)。每个部分都单独被存储在数据字典中。包声明是一个操作接口,对应用来说是可见的。

2)包体是黑盒,对应用来说隐藏了实现细节。

11.2包的组成 将相关的若干程序单元组织到一块,用一个包来标识这个集合,包中可以包含以下的程序单元: 程序单元 过程(procedure) 函数(function) 变量(variable) 游标(cursor) 类型(type) 常量(constant) 异常(exception) 描述 带有参数的程序 带有参数的程序,该程序有返回值 用于存储变化值的存储单元 定义一条SQL语句 定义复合类型(record、collection) 定义常量 标识异常 11.3包的优点 1)方便对存储过程和函数的组织: ①将相关的过程和函数组织在一起。②在一个用户环境中解决命名的冲突问题。 2)方便对存储过程和函数的管理: ①在不改变包的声明定义是可以改变包体的实现的。②限制过程、函数的依赖性。 ③在包体未实现时,其他程序中可以调用包中的对象,对自己的程序进行编译,可以并行地对程序开发。 3)方便对存储过程和函数的安全性管理: ①整个包的访问权限只需一次性授权。②区分公用过程和私有过程。 4)改善性能: ①在包被首次调用时作为一个整体全部调入内存。②减少多次调用时磁盘I/O次数。

11.4 package声明的语法 create or replace package pkg_name {is|as} 公共变量(variable)的定义|公共类型(type)的定义| 公共异常(Exception)的定义|公共游标(cursor)的定义| 函数说明|过程说明 end;

41

11.5 package body声明的语法

create or replace package body pkg_name {is|as} 函数实现;--调用一次执行一次 过程实现;--调用一次执行一次 begin --初始化代码;--首次调用包中的任意对象则执行一次(只执行1次) end; 11.6编译包和包体

alter package pkg1 compile; alter package pkg1 compile body; 11.7案例 eg1:创建一个包并调用 create or replace package pkg_chang--创建package is type t_rec is record(m1 number,m2 varchar2(10)); v_rec t_rec; procedure proc1; function fun1(p_in number)return number; end; --创建package body create or replace package body pkg_chang is procedure proc1--存储在数据库中的package包里 is begin dbms_output.put_line(v_rec.m1); end; function fun1(p_in number)return number is begin return p_in; end; --begin --v_rec.m1 :=100; end; --调用package begin pkg_chang.v_rec.m1 :=pkg_chang.fun1(10); pkg_chang.proc1; end;

42

eg2:创建一个包并调用 --创建package create or replace package pkg_chang1 is type t_rec is record(realname varchar2(30),age number); v_account t_rec; procedure p_account; function f_account(p_id number)return t_rec; end; --创建package body create or replace package body pkg_chang1 is procedure p_account is begin dbms_output.put_line('姓名:'||v_account.realname||' 年龄:'||v_account.age); end; function f_account(p_id number)return t_rec is --v_account t_rec; begin select real_name,round((sysdate-birthdate)/365) into v_account from account where id=p_id; return v_account; end; end; --调用package begin pkg_chang1.v_account :=pkg_chang1.f_account(1005); pkg_chang1.p_account; end; ? 注意事项: ? to_char、nvl等常用函数都在系统提供的standard package包中,只不过可以省略包名,其他包都要写包名;包内部可不写包名,出了包要写。 ? 包里的过程和函数也可以重载。 ? 包声明变了,包体一定要变。

? 可以有包声明,而没有包体,但不能只有包体而没有包声明。 ? 删除包,则包体也被删除了。

? 若package没有更新,只更新了package body,调用包的程序代码不需要修改,

也不需要重新编译。

43

十二、触发器trigger

12.1面临问题

1)在作DML操作时,不需要提供主键值,系统自动生成。 2)如何实现级联更新。

12.2 DML触发器的组成

组成部分 触发时间 触发事件 触发器类型 触发器体 描述 触发事件的时间次序 DML语句是触发事件 触发器体被执行的次数 该触发器将要执行的动作 before、after insert、update、delete statement(语句级)、row(行级) 完整的PL/SQL块 可能值 12.3 DML触发器的类型 1)语句级触发器。 2)行级触发器。 3)行级触发器与语句级触发器的区别:触发的次数不同,如果DML语句只影响一行,则语句级与行级触发器效果一样;如果影响多行,则行级触发器触发的次数比语句级触发器触发的次数多。 12.4 DML触发器的触发顺序 根据触发的时间、类型不同,可以组合为四种DML触发器。 触发时间 before before after after 级别 statement row row statement 描述 在SQL语句执行之前执行一次 SQL语句影响的每条记录被update、delete或insert之前执行一次 SQL语句影响的每条记录被update、delete或insert之后执行一次 在SQL语句执行之后执行一次 ? 注意事项:如在update 表时分为5个执行区间:做update语句之前;修改每一条符合条件的记录之前;修改记录;修改每一条符合条件的记录之后;做完update语句之后; 12.5 DML行级触发器 for each row子句创建一个行级触发器,使其在受到触发事件影响的每一行上都被触发。 eg:语法 create [or replace] trigger trig_name {before|after} insert [or update?] on tab_name for each row [when restricting_condition] PL/SQL block; 12.6 :OLD和:NEW

在行级触发器中,在列名前加上:old标识符表示该列变化前的值,加上:new标识符表示变化后的值(绑定变量)。

44

触发事件 insert update delete :old.列名 所有的字段是null 在update之前该列的原始值 在delete行之前的该列的原始值 :new.列名 insert语句中要插入的值 update语句中要更新的新值 所有字段都是null eg1:自动产生主键值 --创建表 create table test_chang(c1 number primary key,c2 number); --产生序列号 create sequence s1_chang; --创建触发器,以序列号作为c1列的值 create or replace trigger gen_pk_chang before insert on test_chang for each row begin select s1_chang.nextval into :new.c1 from dual; end; --只插入c2列的值,发现不报错了。原因:系统自动将序列号插入c1列 insert into test_chang(c2) values(1); --把c1列的值也插入,发现数字10被序列号替换了,before insert for each row覆盖了:new的值 insert into test_chang values(10,5); 若不想被替换,则匿名块修改为: if :new.c1 is null then select s1_chang.nextval into :new.c1 from dual; end if; ? 注意事项: :new和:old是记录类型。 ? :new和:old只能用于行级触发器,不能用于语句级触发器。 ? :new tabname%rowtype,绑定变量new和定义触发器的table的行记录结构相同。 ? :old tabname%rowtype,绑定变量old和定义触发器的table的行记录结构相同。 ? after行级触发器不可以修改:new的值,但可以查看:new的值。 ? insert into test_chang(c2) values(1); :new.c1=null :new.c2=1 --若没有触发器,则报错。原因:将null插入了主键列。

eg2:实现级联更新 --创建父子表 create table parent_chang(c1 number primary key,c2 number); create table child_chang(c1 number primary key,c2 constraint child_c2_fk references parent_chang(c1)); --向父子表中插入数据 insert into parent_chang values(11,1); insert into parent_chang values(12,2); insert into child_chang values(21,11); 45