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

包中的内容 Oracle?

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

软件包的组件

PL/SQL 包有两个组件。

  • 包装规格
  • 封装主体

包装规格

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

以下是包规范的一些特征。

  • 所有在规范中声明的元素都可以从包外部访问。这样的元素称为公共元素。
  • 包规范是一个独立元素,这意味着它可以独立存在而不需要包主体。
  • 每当一个包引用了该特定会话时,就会创建该包的一个实例。
  • 为会话创建实例后,在该实例中启动的所有包元素在会话结束前都有效。

句法

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

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

封装主体

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

以下是包体的特征。

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

语法:

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>
  • 上述语法显示了包主体的创建。

现在我们将了解如何在程序中引用包元素。

引用包元素

一旦在包中声明并定义了元素,我们就需要引用这些元素来使用它们。

可以通过调用包名称加上元素名称(以句点分隔)来引用包中的所有公共元素,即' 。 “”。

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

在 PL/SQL 中创建包

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

Oracle 通过“包初始化”提供一种工具来初始化包元素或在创建此实例时执行任何活动。

这只不过是在定义所有包元素后写入包主体的一个执行块。当会话中第一次引用包时,将执行此块。

句法

在 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>
  • 上述语法显示了包主体中包初始化的定义。

前向声明

包中的前向声明/引用无非是单独声明私有元素并在包主体的后面部分定义它。

私有元素只有在包主体中声明后才能被引用。因此,使用前向声明。但这种用法并不常见,因为大多数情况下私有元素都是在包主体的第一部分声明和定义的。

前向声明是以下选项之一: Oracle,它不是强制性的,使用或不使用取决于程序员的要求。

前向声明

语法:

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>

上面的语法是前向声明。私有元素在包的前向部分单独声明,并在后向部分定义。

包中的游标使用

与其他元素不同,在包内使用光标时需要小心。

如果在包规范中或包主体的全局部分中定义了游标,则游标一旦打开就会持续存在直到会话结束。

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

超载

重载是指拥有多个同名子程序的概念。这些子程序的参数数量、参数类型或返回类型各不相同,也就是说,名称相同但参数数量不同、参数类型不同或返回类型不同的子程序被视为重载。

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

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

步骤1) 封装规范创建

超载

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 创建包含一个过程和一个函数的包规范。这两个现在是此包的公共元素。

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

超载

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:创建包主体。
  • 代码行 9-16:定义规范中声明的元素“set_record”。这与在 PL/SQL 中定义独立过程相同。
  • 代码行17-24: 定义元素“get_record”。与定义独立函数相同。
  • 代码行25-26: 定义包初始化部分。

步骤3) 参考上面创建的包创建一个匿名块来插入和显示记录。

超载

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: 在匿名块中填充记录类型变量的数据以调用包的“set_record”元素。
  • 代码第 38 行: 已调用 guru99_get_set 包的“set_record”。现在包已实例化,并将持续到会话结束。
  • 由于这是对该包的第一次调用,因此执行包初始化部分。
  • 由“set_record”元素插入到表中的记录。
  • 代码第 41 行: 调用“get_record”元素来显示插入的员工的详细信息。
  • 在对包的“get_record”调用期间,第二次引用该包。但这次不执行初始化部分,因为包已在此会话中初始化。
  • 代码行42-45: 打印员工详细信息。

包中的依赖关系

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

  • 规范是一个独立的对象。
  • 包主体依赖于规范。
  • 包主体可以单独编译。每当规范编译时,主体都需要重新编译,否则它将失效。
  • 包体中依赖于私有元素的子程序应该仅在私有元素声明之后定义。
  • 规范和主体中引用的数据库对象在包编译时需要处于有效状态。

包装信息

一旦创建了包信息,包信息(如包源、子程序详细信息和重载详细信息)即可在 Oracle 数据定义表。

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

表名 描述 询问
所有对象 提供包的详细信息,如 object_id、creation_date、last_ddl_time 等。它将包含所有用户创建的对象。 从所有对象中选择 *,其中 object_name =' ‘
用户对象 提供包的详细信息,如 object_id、creation_date、last_ddl_time 等。它将包含当前用户创建的对象。 从用户对象中选择 *,其中 object_name =' ‘
所有来源 提供所有用户创建的对象的来源。 从 all_source 中选择 *,其中 name=' ‘
用户来源 提供当前用户创建的对象的来源。 从用户源中选择 *,其中名称=' ‘
所有程序 提供所有用户创建的子程序详细信息,如 object_id、过载详细信息等。 从所有过程选择 *
其中 object_name=' ‘
用户程序 提供当前用户创建的子程序详细信息,如 object_id、过载详细信息等。 从用户过程选择 *
其中 object_name=' ‘

UTL 文件 – 概述

UTL 文件是 Oracle 执行特殊任务。这主要用于从 PL/SQL 包或子程序读取和写入操作系统文件。它具有单独的函数来放置信息和从文件中获取信息。它还允许在本机字符集中进行读取/写入。

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

总结

我们现在已经了解了 PL / SQL,现在您应该可以开始进行下面的工作了。

  • PL/SQL 包及其组件
  • 封装的特性
  • 引用和重载包元素
  • 管理包中的依赖项
  • 查看包信息
  • 什么是UTL文件