--######################################--
--包
--王林 2012.07.06
--######################################--
/*
包可以将一些有联系的对象放在其内部。任何能在块定义部分出现的对象都可以在包中出现。这些对象包括存储过程、函数、游标、自定义的类型和变量。可以在其他的块中引用包中的这些对象,因此,包提供了全局变量。一个包由两个独立的部分组成--包头和包体。各部分被单独地存放在数据字典中。定义一个包,要分别定义包头和包体。存储过程或者函数必须在包头中预定义。即在包头中仅定义存储过程或函数以及他们的参数,存储过程或函数的执行代码将在包体中定义。
这不同于无名块中定义存储过程和函数。包体是一个数据字典对象。只有在包头成功编译后,包体才能被编译。包体只包含包头中已预定义的子程序的代码。在包头中定义的对象(不是预定义的)的对象可以直接在包体中使用,不必在包体中定义。如果在包头中有预定义的子程序,则在包体中必须编写其子程序代码,而且包头和包体两部分的子程序必须一致,这包括相同的子程序名、参数和参数类型。与变量类似,包也可以被初始化。只是初始化部分定义在包体的最后部分被定义。
*/
--######################################--
--前向引用
--前向引用占位程序让PL/SQL单遍分析器将过程B的声明放在他的标识符列表中
--JAVA采用的是双遍分析器,所以不用前向声明
DECLARE
--如果将过程B的前向引用去掉,程序引发ORA-00313错误
--因为单遍分析器是自上向下读取的,在分析器读取过程B之前添加B的声明。
--当分析器读取过程A时,他知道有过程B的声明,然后分析器对过程A的调用验证有效性,以后再在程序中查找B的实现,从而编译成功。
PROCEDURE b (caller VARCHAR2);
PROCEDURE a (caller VARCHAR2)
IS
procedure_name VARCHAR2 (1) := 'A';
BEGIN
DBMS_OUTPUT.put_line( 'Procedure ''a'' called by ['
|| caller
|| '].');
b(procedure_name);
END;
PROCEDURE b (caller VARCHAR2)
IS
procedure_name VARCHAR2 (1) := 'B';
BEGIN
DBMS_OUTPUT.put_line( 'Procedure ''b'' called by ['
|| caller
|| '].');
END;
BEGIN
a( 'Main' );
END;
--执行结果:
Procedure 'a' called by [Main].
Procedure 'b' called by [A].
--######################################--
--包头 | 包规范原型
--包规范原型列出的所有组件都是可选的,因为没有任何组件也可以创建包
--在包头声明过的是外部可以调用的过程、函数、变量
--包头是必须的,包体是可选的
--包头必须在包体前创建,否则创建不了包体
CREATE [OR REPLACE] PACKAGE package_name [AUTHID] {DEFINER | CURRENT_USER}
IS | AS
[type_definition;]
[procedure_specification;]
[function_specification;]
[variable_declaration;]
[exception_declaration;]
[cursor_delaration;]
[progma_delaration;]
END package_name;
--######################################--
--包体
--包体是一个独立于包头的数据字典对象,包体只能在包头完成编译后才能编译
--包体内部还可以包括具有包体全局属性的附加声明部分,但这些附加说明对于包体外部是不可见的
CREATE [OR REPLACE] PACKAGE package_name
AS
--全局变量定义;
--函数定义;
--过程定义;
END package_name;
--######################################--
--创建包头
CREATE OR REPLACE PACKAGE demo_package
IS
--包含了一个公用的过程和变量
g_comm NUMBER := 0.10;
PROCEDURE reset_comm(p_comm IN NUMBER);
--注释掉也可以,但是在外部不能调用该函数
/*FUNCTION validate_comm(p_comm IN NUMBER)
RETURN BOOLEAN;*/
END;
--创建包体
CREATE OR REPLACE PACKAGE BODY demo_package
IS
FUNCTION validate_comm(p_comm IN NUMBER)
RETURN BOOLEAN
IS
v_max_comm NUMBER;
BEGIN
SELECT MAX(a.empno)
INTO v_max_comm
FROM scott.emp a;
IF (p_comm > v_max_comm) THEN
RETURN (FALSE);
ELSE
RETURN (TRUE);
END IF;
END validate_comm;
PROCEDURE reset_comm(p_comm IN NUMBER)
IS
BEGIN
IF validate_comm(p_comm) THEN
g_comm := p_comm;
ELSE
raise_application_error(-20210 , 'Expresion is not avilable');
END IF;
DBMS_OUTPUT.put_line('This is the globle variable:' || g_comm);
END reset_comm;
END demo_package;
--SQL Command->编译->执行:
exec demo_package.reset_comm(0);
--执行结果:
This is the globle variable:0
--SQL Command->编译->执行:
exec demo_package.reset_comm(999999);
--执行结果:
begin demo_package.reset_comm(999999); end;
ORA-20210: Expresion is not avilable
ORA-06512: at "SYSTEM.DEMO_PACKAGE", line 23
ORA-06512: at line 2
--查看包内的代码
SET pagesize 49999;
COLUMN line format 99999 heading "Line#";
COLUMN text format A73 heading "Text";
SELECT line , text
FROM User_Source
WHERE UPPER(NAME) = UPPER('&input_name');
--######################################--
--变量
--默认情况下,包是不连续的,当第一个用户调用之后,并不能保证第二个用户调用的仍然是同一个包
--当没有在包规范中声明共享变量或游标时,这个默认情况是很有用的,因为函数和过程是可以重用的。
--当包中含有共享变量时,应总是使包连续可重用
--PRAGMA SERIALLY_REUSABLE (预编译指令)只能在包上下文使用,必须在包规范和主体中都使用它。
--当要共享变量时, PRAGMA SERIALLY_REUSABLE很重要,因为他能保证每次调用他们都处于初始状态。
--E.g:测试共享变量
CREATE OR REPLACE PACKAGE demo_variables
IS
--把PRAGMA SERIALLY_REUSABLE放在包规范中,将包定义为连续可重用,那么该案例结果永远是3
--之所以这样,是因为每次对包的调用都会得到一个最新的副本。连续可重用包会重新初始化共享变量的值。
--常量和连续可重用变量之间的唯一区别是常量的值是永远不能改变,而变量的值是可以改变的
--当包为连续可重用时,接下来对包的任何调用就不会再改变值
--包变量应当总是常量
/*PRAGMA SERIALLY_REUSABLE;*/
protecte CONSTANT NUMBER := 1;
unprotecte NUMBER := 1;
END demo_variables;
--
CREATE OR REPLACE PROCEDURE demo_change(n_value NUMBER)
IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
demo_variables.unprotecte := demo_variables.unprotecte + n_value;
Dbms_Output.put_line(demo_variables.unprotecte);
END demo_change;
--执行下去依次递增
--SQL Command->编译->执行:
exec demo_change(2);
--执行结果:
3
--######################################--
--类型
--%:属性指示器
--%ROWTYPE: 将变量的数据类型锚定到数据库目录对象上或PL/SQL记录结构的行结构上。当锚定到目录对象上时,新变量继承了在引用表或视图中列的位置和数据类型。新变量继承了显式PL/SQL记录结构的位置和数据结构,而该位置和数据类型可能是间接的从一个或多个目录对象继承的。
--%TYPE: 将变量的数据类型锚定到在数据库目录对象(表或视图)中列的数据类型。