Oracle PL/SQL Package:类型、规范、主体 [示例]

Oracle 中的 Package 是什么?

PL/SQL package 是将相关的子程序(过程/函数)逻辑分组到单个元素中。Package 被编译并存储为数据库对象,以后可以使用。

Package 的组成部分

PL/SQL package 有两个组成部分。

  • Package 规范
  • Package 主程序

Package 规范

Package 规范包含所有公共 变量、游标、对象、过程、函数和异常的声明。

以下是 Package 规范的几个特点。

  • 在规范中声明的所有元素都可以从 Package 外部访问。这些元素称为公共元素。
  • Package 规范是一个独立的对象,这意味着它可以独立存在而无需 Package 主程序。
  • 每当引用一个 Package 时,都会为该特定会话创建一个 Package 的实例。
  • 在会话创建实例后,该实例中初始化的所有 Package 元素在会话结束前都有效。

语法

CREATE [OR REPLACE] PACKAGE <package_name> 
IS
<sub_program and public element declaration>
.
.
END <package name>

上述语法显示了 Package 规范的创建。

Package 主程序

它包含 Package 规范中存在的所有元素的定义。它还可以包含规范中未声明的元素的定义,这些元素称为私有元素,只能从 Package 内部调用。

以下是 Package 主程序的特点。

  • 它应包含规范中声明的所有子程序/游标的定义。
  • 它还可以包含未在规范中声明的更多子程序或其他元素。这些称为私有元素。
  • 它是一个依赖对象,依赖于 Package 规范。
  • 每当规范被编译时,Package 主程序的状态会变为“无效”。因此,每次编译规范后都需要重新编译它。
  • 私有元素在使用前必须在 Package 主程序中定义。
  • Package 的第一部分是全局声明部分。这包括对整个 Package 可见的变量、游标和私有元素(前向声明)。
  • Package 的最后一部分是 Package 初始化部分,它在会话首次引用 Package 时执行一次。

语法

CREATE [OR REPLACE] PACKAGE BODY <package_name>
IS
<global_declaration part>
<Private element definition>
<sub_program and public element definition>
.
<Package Initialization> 
END <package_name>
  • 上述语法显示了 Package 主程序的创建。

现在我们将学习如何在程序中引用 Package 元素。

引用 Package 元素

一旦元素在 Package 中声明和定义,我们就需要引用这些元素才能使用它们。

Package 的所有公共元素都可以通过调用 Package 名称后跟元素名称,并用句点分隔来引用,即“.”。

Package 的公共变量也可以用相同的方式使用,用于分配和获取值,即“.”。

在 PL/SQL 中创建 Package

在 PL/SQL 中,每当在会话中引用/调用 Package 时,都会为该 Package 创建一个新的实例。

Oracle 提供了一种设施,可以在创建实例时初始化 Package 元素或执行任何活动,通过“Package 初始化”。

这不过是一个执行块,写在 Package 主程序中,定义完所有 Package 元素之后。每当在会话中首次引用 Package 时,都会执行此块。

语法

Create Package in PL/SQL

CREATE [OR REPLACE] PACKAGE BODY <package_name>
IS
<Private element definition>
<sub_program and public element definition>
.
BEGINE
<Package Initialization> 
END <package_name>
  • 上述语法显示了 Package 主程序中 Package初始化的定义。

前向声明

Package 中的前向声明/引用不过是单独声明私有元素,并在 Package 主程序的后续部分中定义它。

私有元素只有在 Package 主程序中已声明的情况下才能引用。为此,使用了前向声明。但它不太常用,因为在大多数情况下,私有元素在 Package 主程序的第一部分声明和定义。

前向声明是 Oracle 提供的一种选项,它不是强制性的,是否使用取决于程序员的要求。

Forward Declarations

语法

CREATE [OR REPLACE] PACKAGE BODY <package_name>
IS
<Private element declaration>
.
.
.
<Public element definition that refer the above private element>
.
.
<Private element definition> 
.
BEGIN
<package_initialization code>; 
END <package_name>

上述语法显示了前向声明。私有元素在 Package 的前向部分单独声明,并在后续部分定义。

Package 中游标的使用

与其他元素不同,在使用 Package 中的游标时需要小心。

如果游标在 Package 规范或 Package 主程序的全局部分定义,那么一旦打开,游标将一直存在直到会话结束。

因此,在引用游标之前,应始终使用游标属性 '%ISOPEN' 来验证游标的状态。

重载

重载是拥有多个同名子程序的概念。这些子程序将通过参数的数量、参数的类型或返回类型来区分彼此,即同名但参数数量不同、参数类型不同或返回类型不同的子程序被视为重载。

当许多子程序需要执行相同任务,但调用它们的方式应该不同时,这很有用。在这种情况下,所有子程序的名称将保持相同,并且参数将根据调用语句进行更改。

示例 1:在此示例中,我们将创建一个 Package 来获取和设置 'emp' 表中员工信息的值。get_record 函数将为给定的员工编号返回记录类型输出,set_record 过程将记录类型记录插入到 emp 表中。

步骤 1) 创建 Package 规范

Overloading

CREATE OR REPLACE PACKAGE guru99_get_set
IS
PROCEDURE set_record (p_emp_rec IN emp%ROWTYPE);
FUNCTION get record (p_emp no IN NUMBER) RETURN emp%ROWTYPE;
END guru99_get_set:
/

输出

Package created

代码解释

  • 代码第 1-5 行:为 guru99_get_set 创建 Package 规范,包含一个过程和一个函数。这两个现在是此 Package 的公共元素。

步骤 2) Package 包含 Package 主程序,其中将定义所有过程和函数的实际定义。在此步骤中,创建了 Package 主程序。

Overloading

CREATE OR REPLACE PACKAGE BODY guru99_get_set
IS	
PROCEDURE set_record(p_emp_rec IN emp%ROWTYPE)
IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO emp
VALUES(p_emp_rec.emp_name,p_emp_rec.emp_no; p_emp_rec.salary,p_emp_rec.manager);
COMMIT;
END set_record;
FUNCTION get_record(p_emp_no IN NUMBER)
RETURN emp%ROWTYPE
IS
l_emp_rec emp%ROWTYPE;
BEGIN
SELECT * INTO l_emp_rec FROM emp where emp_no=p_emp_no
RETURN l_emp_rec;
END get_record;
BEGUN	
dbms_output.put_line(‘Control is now executing the package initialization part');
END guru99_get_set:
/

输出

Package body created

代码解释

  • 代码第 7 行:创建 Package 主程序。
  • 代码第 9-16 行:定义规范中声明的元素 'set_record'。这与在 PL/SQL 中定义独立过程相同。
  • 代码第 17-24 行:定义元素 'get_record'。这与定义独立函数相同。
  • 代码第 25-26 行:定义 Package 初始化部分。

步骤 3) 创建一个匿名块,通过引用上面创建的 Package 来插入和显示记录。

Overloading

DECLARE
l_emp_rec emp%ROWTYPE;
l_get_rec emp%ROWTYPE;
BEGIN
dbms output.put line(‘Insert new record for employee 1004');
l_emp_rec.emp_no:=l004;
l_emp_rec.emp_name:='CCC';
l_emp_rec.salary~20000;
l_emp_rec.manager:=’BBB’;
guru99_get_set.set_record(1_emp_rec);
dbms_output.put_line(‘Record inserted');
dbms output.put line(‘Calling get function to display the inserted record'):
l_get_rec:=guru99_get_set.get_record(1004);
dbms_output.put_line(‘Employee name: ‘||l_get_rec.emp_name);
dbms_output.put_line(‘Employee number:‘||l_get_rec.emp_no);
dbms_output.put_line(‘Employee salary:‘||l_get_rec.salary');
dbms output.put line(‘Employee manager:‘||1_get_rec.manager);		
END:
/

输出

Insert new record for employee 1004
Control is now executing the package initialization part
Record inserted
Calling get function to display the inserted record
Employee name: CCC
Employee number: 1004
Employee salary: 20000
Employee manager: BBB

代码解释

  • 代码第 34-37 行:在匿名块中填充记录类型变量的数据,以调用 Package 的 'set_record' 元素。
  • 代码第 38 行:调用 guru99_get_set Package 的 'set_record'。现在 Package 已实例化,并将一直存在直到会话结束。
  • 由于这是第一次调用 Package,Package 初始化部分将被执行。
  • 记录由 'set_record' 元素插入到表中。
  • 代码第 41 行:调用 'get_record' 元素以显示已插入员工的详细信息。
  • 在 'get_record' 调用 Package 时,Package 被第二次引用。但这次初始化部分不会执行,因为 Package 已在该会话中初始化。
  • 代码第 42-45 行:打印员工详细信息。

Package 中的依赖关系

由于 Package 是相关事物的逻辑分组,因此它存在一些依赖关系。以下是需要注意的依赖关系。

  • 规范是独立的对象。
  • Package 主程序依赖于规范。
  • Package 主程序可以单独编译。每当规范被编译时,主程序都需要重新编译,因为它会变得无效。
  • Package 主程序中依赖于私有元素的子程序只能在私有元素声明之后定义。
  • 在 Package 编译时,规范和主程序中引用的数据库对象必须处于有效状态。

Package 信息

一旦创建了 Package 信息,Package 信息,如 Package 源代码、子程序详细信息和重载详细信息,就可在 Oracle 数据定义表中获得。

下表给出了数据定义表和表中可用的 Package 信息。

表名 描述 查询
ALL_OBJECT 提供 Package 的详细信息,如 object_id、creation_date、last_ddl_time 等。它将包含所有用户创建的对象。 SELECT * FROM all_objects where object_name ='<package_name>’
USER_OBJECT 提供 Package 的详细信息,如 object_id、creation_date、last_ddl_time 等。它将包含当前用户创建的对象。 SELECT * FROM user_objects where object_name ='<package_name>’
ALL_SOURCE 提供所有用户创建的对象的源代码。 SELECT * FROM all_source where name='<package_name>’
USER_SOURCE 提供当前用户创建的对象的源代码。 SELECT * FROM user_source where name='<package_name>’
ALL_PROCEDURES 提供所有用户创建的子程序详细信息,如 object_id、重载详细信息等。 SELECT * FROM all_procedures
Where object_name='<package_name>’
USER_PROCEDURES 提供当前用户创建的子程序详细信息,如 object_id、重载详细信息等。 SELECT * FROM user_procedures
Where object_name='<package_name>’

UTL FILE – 概述

UTL File 是 Oracle 提供的用于执行特殊任务的独立实用程序包。它主要用于从 PL/SQL 包或子程序读取和写入操作系统文件。它具有将信息放入文件和从文件获取信息的专用函数。它还允许以本地字符集进行读/写。

程序员可以使用它来写入任何类型的操作系统文件,文件将直接写入数据库服务器。在写入时将指定名称和目录路径。

摘要

我们已经学习了 PL/SQL 中的 Package,现在您应该能够进行以下操作。

  • PL/SQL Package 及其组成部分
  • Package 的特点
  • 引用和重载 Package 元素
  • 管理 Package 中的依赖关系
  • 查看 Package 信息
  • UTL File 是什么