包头
声明包体的内容,里面声明的变量或类型,作用范围是全局的。定义了过程或函数的时候,这个函数也可以被外界使用,如果包头中没定义包体中的函数或过程,这时函数和过程就是这个包私有的。创建时不需要写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包体(因而断开了依赖链)。