PLSQL——08、包

时间:2021-11-29 01:39:30

包头

声明包体的内容,里面声明的变量或类型,作用范围是全局的。定义了过程或函数的时候,这个函数也可以被外界使用,如果包头中没定义包体中的函数或过程,这时函数和过程就是这个包私有的。创建时不需要写begin,即使有begin内容也只在编译是执行一次。

格式如下:

create or replace package P_NAME
is|as
    xxx;
end;

包体

包含这个包的功能(函数+过程),包头定义的函数或者过程必须出现在包体中;而且,包体的包参数上要和包头中的声明完全一致(参数名和参数数据类型),否则就不是同一个对象。

格式如下:

procedure xxx(a number,b varchar2(10))
is
......
begin
  ......
end;

包的创建

创建包头和包体是同名字的,但创建的关键字不同,如:
包头:create package NAME
包体:create package body NAME

演示如下:

--创建package header
CREATE OR REPLACE PACKAGE emp_pack 
IS
  PROCEDURE new_emp
    (v_ename   emp.ename%TYPE,
     v_job     emp.job%TYPE     DEFAULT SALESMAN,
     v_mgr     emp.mgr%TYPE     DEFAULT 7839, 
     v_sal     emp.sal%TYPE     DEFAULT 1000,
     v_comm    emp.comm%TYPE    DEFAULT 0,
     v_deptno  emp.deptno%TYPE  DEFAULT 30);
END emp_pack;
/

--创建package body
CREATE OR REPLACE PACKAGE BODY emp_pack 
IS
  FUNCTION valid_deptno --私有函数
    (v_deptno IN dept.deptno%TYPE)
    RETURN BOOLEAN
  IS    
    v_dummy  VARCHAR2(1);
  BEGIN
    SELECT x
    INTO   v_dummy
    FROM   dept
    WHERE  deptno = v_deptno;
    RETURN (TRUE);
  EXCEPTION
    WHEN NO_DATA_FOUND THEN 
      RETURN(FALSE);
  END valid_deptno;

  PROCEDURE new_emp
    (v_ename   emp.ename%TYPE,
     v_job     emp.job%TYPE     DEFAULT SALESMAN,
     v_mgr     emp.mgr%TYPE     DEFAULT 7839,
     v_sal     emp.sal%TYPE     DEFAULT 1000,
     v_comm    emp.comm%TYPE    DEFAULT 0,
     v_deptno  emp.deptno%TYPE  DEFAULT 30)
  IS
  BEGIN
    IF valid_deptno(v_deptno) THEN
      INSERT INTO emp
      VALUES (seq_empno.NEXTVAL, v_ename, v_job, v_mgr, 
              TRUNC (SYSDATE, DD), v_sal, v_comm, v_deptno);
    ELSE
      DBMS_OUTPUT.PUT_LINE(Invalid department number. Try again.);
    END IF;
  END new_emp;
END emp_pack;

重载

包体中过程或函数的参数与包头中的声明的不一致,参数名或参数数据类型不一致都属于重载。

演示如下:

--创建package header
CREATE OR REPLACE PACKAGE over_load is
  FUNCTION print_it(v_arg date)
    RETURN VARCHAR2;
  FUNCTION print_it(v_arg VARCHAR2)
    RETURN VARCHAR2;
END over_load;
/

--创建package body
CREATE OR REPLACE PACKAGE BODY over_load is
  FUNCTION print_it(v_arg date)
    RETURN VARCHAR2
  IS
  BEGIN
    RETURN to_char(v_arg,yyyy-mm-dd);
  END print_it;
         
  FUNCTION print_it(v_arg VARCHAR2)
    RETURN VARCHAR2
  IS
  BEGIN
    RETURN to_char(v_arg,L99,999.00);
  END print_it;
END over_load;
/

--包的调用
VARIABLE g_datevalue varchar2(40);
execute :g_datevalue := over_load.PRINT_IT(sysdate); 
print g_datevalue

VARIABLE g_datevalue varchar2(40);
execute :g_datevalue := over_load.PRINT_IT(123); 
print g_datevalue

包与依赖

先来看看使用存储过程和函数时的依赖情况。

----创建表
create table t (x int);
----创建视图
create view v as select * from t;
----创建存储过程
create procedure p
is
begin
    for x in (select * from v)
    loop
        null;
    end loop;
end;
----创建函数
create function f return number
is
    l_cnt number;
begin
    select count(*) into l_cnt from t;
    return l_cnt;
end;

查看依赖关系

select name,type,referenced_name,referenced_type
from user_DEPENDENCIES
where name IN (F,P,T,V)
AND referenced_owner=SCOTT
order by name;

此时对象状态都为有效。修改T表结构后,再看看对象的有效性:

----修改T表结构
alter table t add y number;
----查看对象有效性
select object_name,object_type,status 
from user_objects
where object_name in (F,P,T,V);

结果为:V、P、F对象无效,因为他们都依赖于表T。

创建另一个存储过程P2,其依赖于P,再看看有效性:

----创建P2
create procedure p2 
is
begin
p;
end;
----查看有效性
select object_name,object_type,status 
from user_objects
where object_name in (F,P,P2,T,V);

结果为:V、P重新有效,因为对P2的编译会同时重新编译V、P。

使用包重做上面的实验:

----创建包package1
create package pk1
is
    procedure p;
end;

create package body pk1
is
  procedure p
  is
    begin
    for x in (select * from v) --依赖于视图V
    loop
        null;
    end loop;
    end;
end pk1;

----创建package2
create package pk2
is
    procedure p;
end;

create package body pk2
is
  procedure p
  is
    begin
    pk1.p;    --引用的不是pk1的包体,而是包头
    end;
end pk2;

再次修改T的表结构,再查看有效性。发现PK1 包体无效,因为PK1 包体通过视图V依赖于表T(依赖链);而包PK2仍然有效,PK2依赖于PK1而不是PK1包体(因而断开了依赖链)。