(转自:http://blog.csdn.net/bbliutao/article/details/9016947)

一、概述
包可将一些有联系的对象放在其内部。
任何能在块定义部分出现的对象都可以在包中出现。这些对象包括存储过程、函数、游标、自定义的类型和变量。
我们可以在其它的PLSQL块中引用包中的这些对象,也就是说,包为PLSQL提供了全局变量。
包可以实现信息屏蔽和子程序重载。

二、包的定义
一个包由两个独立部分组成——包头和包体,各部分被单独存放在数据字典中,定义一个包,要分别定义包头和包体。

1、定义包头
(1)、语法:
create or replace package [schema.] package_name {is|as}
pl/sql_package;

其中package_name是包的名称,pl/sql_package可以是存储过程、函数、变量、类型、异常及游标的定义。

存储过程和函数必须在包头中预定义。也就是说,在包头中仅定义存储过程名或函数名以及它们的参数,
存储过程或函数的执行代码将在包体中定义,这不同于无名块中定义存储过程和函数。

例一:
create or replace package aupack as
procedure qusalary(p_code auths.code%type,p_salary out auths.salary%type);
procedure inauthor(p_code auths.code%type,p_salary out auths.salary%type);
end aupack;

2、定义包体
包体是有个数据字典对象。只有在包头成功编译后,包体才能被编译。包体只包含包头中已预定义的子程序代码。
在包头中定义(不是预定义)的对象可以直接在包体中使用,不必再在包体中定义。

(1)、语法:
create or replace package body [schema.] package_name {is/as}
pl/sql_body;

其中package_name为包名,该包名必须与定义包头时的名称一样,pl/sql_body为过程与函数的定义

如果包头不包含存储过程和函数,则不必定义包体。如果包头中有预定义的子程序(存储过程和函数),
则在包体中必须编写其子程序代码,而且包头和包体两部分指定的子程序必须一致。
这包括相同的子程序名、参数名、和参数类型。

例一:
create or replace package body aupack is
procedure qusalary(p_code auths.code%type,p_salary out auths.salary%type) as
begin
...
end aupack;
procedure inauthor(p_code auths.code%type,p_salary out auths.salary%type) as
begin
...
end inauthor;

三、包的初始化

1、概述
包的初始化,也称包的构造过程。类似于C++的构造函数和JAVA的构造方法。
当在会话内第一次被调用时会自动执行,并且在同一会话内只会执行一次。
包的构造过程没有名称,在包体实现了包头定义的子程序后的最后部分被定义,以begin开始、end结束。

2、语法
create or replace package body package_name {is|as}
... --实现包体中过程与函数的定义部分
begin
... --初始化代码
end;

例子
create or replace package apack as
v_sex varchar2(2);
procedure a_sex(p_code auths.code%type,p_sex out varchar2);
end apack;

create or replace body apack as
procedure a_sex(p_code auths.code%type,p_sex out varchar2) as
v_sex number;
begin
select sex into v_sex from auths where code=p_code;
if v_sex=0 then
p_sex:='女';
else
p_sex:='男';
end if;
end apack;
begin
a_sex('A0001',v_sex); --初始化部分
end apack;

四、包中子程序的重载
1、定义
在包的内部,存储过程和函数都可被重载,这意味着有多个存储过程或函数可以使用同一名称,但是参数不能相同。
这样就允许不同的参数调用同一名字的过程或函数。但需要注意同名的函数返回值得数据类型必须完全相同。

例一
create or replace package pac_temp is
procedure ad(p_in number);
procedure ad(p_in varchar2);
end pac_temp;

create or replace package body pac_temp is
procedure ad(p_in number) as
begin
dbms_output.put_line(p_in);
dbms_output.put_line('我是number');
end ad;

procedure ad(p_in varchar2) as
begin
dbms_output.put_line(p_in);
dbms_output.put_line('我是varchar2');
end ad;
end pac_temp;

begin
dbms_output.put_line('a');
pac_temp.ad('5');
pac_temp.ad(5);
end;

2、重载的局限性
(1)、当仅仅参数名不同或者是模式(in,out,in out)不同时,不能重载。
(2)、不能对仅有返回类型不同的函数进行重载。
(3)、重载函数的参数必须是数据类型不同间不可自动转换的。此类错误在编译时不会报错,但调用若自动转换发生错误会报错。

五、包的删除
drop package body pac_name; --只删除包体
drop package pac_name; --删除包

六、包函数的使用纯度级别

1、概述
首先,应该明确这里说的是包里面的函数。要在sql语句中引用包的函数,那么该函数不能含有DML语句,也不能读写远程包的变量。
为了对包的函数加以限制,在定义包头时使用可以使用纯度级别限制函数。

2、语法
pragma restrict_references(function_name,wnds[,wnps,rnds,rnps]); --能在sql语句中调用的函数都有wnds基本约束

四种约束的含义:
WNDS:函数内不能修改数据库数据(即禁止DML操作)。
WNPS:函数内不能修改包变量(即不能给包变量赋值)。
RNDS:函数内不能读取数据数据(即禁止执行select操作)。
RNPS:函数内不能读取包变量(即不能将包变量赋值给其他变量)。

根据对函数的四种基本约束,满足下面约束的函数可以被sql语句调用:
(1)、当函数满足WNDS时,能被SQL语句调用。
(2)、当函数满足RNPS和WNPS约束时,这个函数(通过数据库连接)能被远程或并行调用。
(3)、在select、valules或set子句中调用的函数可以没有wnps约束,但在其它的子句中就必须满足wnps约束。
(4)、一个函数所调用的子程序与该函数的约束级别相同。
(5)、在含有create table或alter table 命令的check子句中,不能调用直接创建的函数,因为这些语句中的定义不能变化。

3、restrict_references编译指令的应用

当将单独存储在数据库中的函数用在sql语句时,plsql引擎自动确定该函数有哪些约束,这些约束能否保证函数在sql语句中的调用。
对于包函数,则先在包内部使用restrict_references编译指令来指定包函数的约束。
在sql语句中调用这个包函数时,plsql根据编译指令指定的约束来来判断包函数能否在sql语句中调用。

例一:
create or replace package authorpack as
function fun(p_authorcode auths.author_code%type) return varchar2;
pragma restrict_references(fun,WNDS,WNPS,RNPS); --指定3种约束
function authorcount return number;
pragma restrict_references(authorcount,WNDS,WNPS,RNPS)
end authorpack;

create or replace body authorpack as
v_num number; --包变量
function fun(p_authorcode auths.author_code%type) return varchar2 as
v_return varchar2(16);
begin
select author||name into v_return from auths where author_code=p_authorcode;
return v_return;
end fun;

function authorcount return number as
v_return number;
begin
if v_num is null then --包变量被读,不满足RNPS
select count(*) into v_return from auths where author_code like 'A%';
v_num:=v_return; --包变量被修改,不满足WNPS
else
v_return:=v_num;
end if;
return v_return;
end authorcount;
end authorpack;

例二
create or replace package purity is
minsal number(6,2);
maxsal number(6,2);
function max_sal return number;
function min_sal return number;
pragma restrict_references(max_sal,WNPS);
pragma restrict_references(min_sal,WNPS);
end;

create or replace package body purity is
function max_sal return number is
begin
select max(sql) into maxsal from emp; --违反WNPS
return maxsal;
end;
function min_sal return number is
begin
select max(sql) into maxsal from emp; --违反WNPS
return maxsal;
end;
end;

在包头的fun函数中使用了restrict_references编译指令指定约束,包体中的fun函数代码显然符合编译指令指定的约束——没有修改数据库表、没有读包中变量、没有修改包中变量。
在包头的authorcount函数中使用了restrict_references编译指令,但在包体中的authorcount函数代码
并不符合指定的约束,包体中定义的变量v_num不仅仅被该函数读出(不满足rnps约束),而且被修改(不满足wnps约束),
所以plsql引擎编译到函数authorcount是,会报“pls-00452”的错误。

注意事项:
(1)、plsql编译程序根据编译指令来确认包函数的基本约束,从而确认这个包函数是否能在sql语句中使用。
只要随后修改了包体(或第一次创建),就要按照编译指令校检函数代码。
(2)、包初始化部分的代码同样也可以有基础约束。包的基本约束也用restrict_references来指定,
但应以包名为参数而不是以函数名为参数。
(3)、restrict_references可在包中函数定义后的任何位置出现,但它只能约束一个函数的定义。
因此,对于函数的重载,pragma只约束最近定义的函数。
(4)、如果函数中用到了系统包,则该函数不能用在sql语句中。常见系统包dbms_output,dbms_pipe,dbms_alter,dbms_sql,utl_file等。
(5)、当过程性语句中调用一个函数时,可以使用参数缺省值,而通过sql语句调用一个函数时,所有的参数都必须指定。
另外必须使用位置表示法,而不能使用名称表示法。

05-28 18:02