天天看点

ORACLE PL/SQL包(package)学习笔记

包由包规范和包体两部分组成。

1、包规范(Package Specification)

包规范,也叫做包头,包含了有关包的内容的信息。但是,它不包含任何过程的代码。

创建包头的语法一般如下

CREATE [OR REPLACE] PACKAGE package_name {IS | AS}

Procedure_name | function_name | variable_declaration | type_definition | exception_declaration | cursor_declaration

END [package_name];

声明包头还要遵循一些语法规则,如下:

  • 包部件可以以任意次序出现。但是,对象必须在被引用之前进行声明。
  • 所有类型的部件都没有必要都被使用。例如,包可以仅包含过程和函数规范,而没有声明异常处理或类型。
  • 对于过程和函数的所有声明都必须是前向声明。

2、包主体(Package Body)

包主体和包头存储在不同的数据字典中。如果没有对包头进行成功的编译,就不可能对包主体编译成功。主体中包含了在包头中前向子程序声明相应的代码。

包主体是可选的。如果包头不包含任何过程或函数,那么包主体可以没有。这个技术对于声明全局变量是很有用的,因为包中的所有对象在包的外面是可见的。

包头中的所有前向声明必须在包主体中被更新。过程或函数的规范在包头和包主体中必须是相同的。这个规范包括子程序的名字、参数的名字以及参数的模式。

3、包和作用域

在包头中定义的任何对象都有一定的范围,在包以外通过使用包名称限定仍然可以使用这些对象。例如,可以像下面PL/SQL块那样调用InventoryOps.DeleteISBN过程。

BEGIN

InventoryOps.DeleteISBN(‘78824389’);

END;

包过程的调用与单独的过程调用相同,唯一的区别就是在包过程的前面添加了包名称前缀。包过程可以带有默认的参数,可以使用位置表示法或者名称表示法调用它们,就像单独的存储过程一样。

包头中的对象在包主体中可以直接使用,不需要附带包名前缀。

4、包子程序的重载

在包中,过程和函数是可以重载的。这也就意味着可以让多个过程或函数共用同一个名称,但是带有不同的参数。这是一个非常有用的功能特性,因为它让同一个操作可以执行在不同类型的对象上。

下面示例演示了包子程序的重载

CREATE OR REPLACE PACKAGE InventoryOps AS

-- Returns an array containing the books with the specified status.

PROCEDURE StatusList(p_Status IN inventory.status%TYPE,

p_Books OUT t_ISBNTable,

p_NumBooks OUT BINARY_INTEGER);

TYPE c_ISBNCur IS REF CURSOR;

-- Returns an opened cursor containing the books with the specified status.

PROCEDURE StatusList(p_Status IN inventory.status%TYPE, --重载

p_BookCur OUT c_ISBNCur);

END InventoryOps;

/

CREATE OR REPLACE PACKAGE BODY InventoryOps AS

-- Returns an array containing the books with the specified status.

PROCEDURE StatusList(p_Status IN inventory.status%TYPE,

p_Books OUT t_ISBNTable,

p_NumBooks OUT BINARY_INTEGER) IS

v_ISBN inventory.isbn%TYPE;

CURSOR c_Books IS

SELECT isbn

FROM inventory

WHERE status = p_Status;

BEGIN

ValidateStatus(p_Status);

/* p_NumBooks will be the array index. It will start at

* 0, and be incremented each time through the fetch loop.

* At the end of the loop, it will have the number of rows

* fetched, and therefore the number of rows returned in

* p_Books. */

p_NumBooks := 0;

OPEN c_Books;

LOOP

FETCH c_Books INTO v_ISBN;

EXIT WHEN c_Books%NOTFOUND;

p_NumBooks := p_NumBooks + 1;

p_Books(p_NumBooks) := v_ISBN;

END LOOP;

CLOSE c_Books;

END StatusList;

-- Returns an opened cursor containing the books with the specified status.

PROCEDURE StatusList(p_Status IN inventory.status%TYPE,

p_BookCur OUT c_ISBNCur) IS

BEGIN

ValidateStatus(p_Status);

OPEN p_BookCur FOR

SELECT isbn

FROM inventory

WHERE status = p_Status;

END StatusList;

END InventoryOps;

/

当需要不同类型参数执行同一操作时,重载这种技术就非常有用。但是,重载也有一些限制:

  • 如果两个子程序只在参数的名称和模式上不同的时候,就不能重载这两个子程序。
  • 如果两个函数只在返回类型上存在不同的时候,就不能重载这两个函数。
  • 重载函数的参数必须分别属于不同的类型系列—不能在同一类型系列的参数上使用重载。

5、包的初始化

首次调用包子程序,或引用了包的任意变量或数据类型的时候,包就被实例化。实例化就意味着系统已将包从磁盘读取到内存中,被调用子程序的已编译代码也正在运行。此时,将为包中定义的所有变量分配内存。每一个会话都有包变量的一份副本,这保证了执行同一包的同一子程序的两个会话使用的是不同的内存位置。

在大多数情况下,某个会话中的包一旦被实例化,都需要立即运行初始化代码。这可以通过在包主体中添加一个初始化部分来实现。初始化部分的位置是在其他所有对象以后,初始化的语法如下:

CREATE OR REPLACE PACKAGE BODY package_name {IS | AS}

BEGIN

Initialization_code;

END [pckage_name];

6、缺省参数

当从过程性的语句中调用函数的时候,如果需要的话,你可以为形式参数设置缺省值。但是当从SQL语句中调用函数的时候,必须为所有的参数指定缺省值。而且,只能使用位置标识法而不是带名标识法指定缺省值。下面对于FullName的调用就是非法的。

SELECT FullName(p_StudentID=>10000) FROM dual;