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

⚡ 智能摘要

PL/SQL 包将相关的过程、函数、变量、游标和异常分组到一个包含规范和主体的模式对象中。规范声明公共接口,而主体包含私有实现,从而提高了模块化和性能。

  • 📦 软件包定义: 包是一个逻辑组ping 将相关的子程序和对象编译并存储为单个数据库对象。
  • 📋 包装规格: 声明可从包外部访问的公共变量、游标、类型、异常、过程和函数。
  • 🧱 包装主体: 定义规范中声明的每个元素,以及只能从包内部调用的私有元素。
  • 🔁 重载: 当多个子程序的参数数量、参数类型或返回类型不同时,它们可以共用一个名称。
  • 🔗 指称和依赖: 公共元素被命名为 package_name.element_name,其主体取决于规范。
  • 🤖 人工智能协助: GitHub Copilot 等 AI 助手可以根据注释草拟软件包规范、主体和重载子程序。

Oracle PL/SQL 包规范和主体结构概述

包中的内容 Oracle?

Oracle PL / SQL 包是一个逻辑组ping 相关的 子程序 将(过程/函数)合并成一个单独的元素。该包会被编译并存储为数据库对象,以便后续重用。

软件包的组件

PL/SQL 包包含两个组件。

  • 包装规格
  • 封装主体

包装规格

软件包规范包含所有公共组件的声明 变量, 游标对象、过程、函数和 例外.

以下是该封装规格的一些特点。

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

句法

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

以上语法展示了如何创建软件包规范。

封装主体

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

以下是包装体的特征。

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

语法:

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>
.
BEGIN
<Package Initialization> 
END <package_name>

上述语法显示了包主体中包初始化的定义。

前向声明

包中的前向声明或引用,实际上就是将私有元素单独声明,并在包体的后半部分定义它们。

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

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

下面的屏幕截图显示了私有元素是如何在包主体中预先声明并随后定义的。

在私有元素中向前声明 Oracle PL/SQL 包主体

语法:

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)创建软件包规格

下面的屏幕截图显示了正在创建的 guru99_get_set 包规范。 Oracle.

创建 guru99_get_set 包规范,其中包含 set_record 和 get_record。 Oracle

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

Code 说明

  • Code 第 1-5 行: 创建了包含一个过程和一个函数的 guru99_get_set 包规范。这两个过程和函数现在是该包的公共元素。

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

下面的屏幕截图显示了 guru99_get_set 包体的定义。 Oracle.

定义 guru99_get_set 包体,其中包含 set_record、get_record 和初始化块。

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;
BEGIN
dbms_output.put_line('Control is now executing the package initialization part');
END guru99_get_set;
/

输出:

Package body created

Code 说明

  • Code 第7行: 创建包主体。
  • Code 第 9-16 行: 定义规范中声明的元素“set_record”。这与在PL/SQL中定义独立过程相同。
  • Code 第 17-24 行: 定义元素“get_record”。这与定义一个独立函数的效果相同。
  • Code 第 25-26 行: 定义包初始化部分。

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

下面的屏幕截图显示了调用该包的匿名代码块及其输出。 Oracle.

匿名块调用 guru99_get_set 插入并显示员工记录及其输出

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:=1004;
l_emp_rec.emp_name:='CCC';
l_emp_rec.salary:=20000;
l_emp_rec.manager:='BBB';
guru99_get_set.set_record(l_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:'||l_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

Code 说明:

  • Code 第 34-37 行: 在匿名块中填充记录类型变量的数据,以调用包的“set_record”元素。
  • Code 第38行: 已调用 guru99_get_set 包的 'set_record' 函数。现在该包已实例化,并将持续到会话结束。由于这是首次调用该包,因此会执行包初始化部分,并通过 'set_record' 函数将记录插入到表中。
  • Code 第41行: 调用“get_record”元素以显示插入员工的详细信息。此调用期间第二次引用了该包,但由于该包已在此会话中初始化,因此不会再次执行初始化部分。
  • Code 第 42-45 行: 打印员工详细信息。

包中的依赖关系

由于该软件包是一个逻辑组ping 它与一些相关事物存在依赖关系。以下是需要注意的依赖关系。

  • 规范是一个独立对象。
  • 封装体取决于具体规格。
  • 包体可以单独编译。每当规范编译完毕后,包体也需要重新编译,因为它会失效。
  • 包体中依赖于私有元素的子程序应该只在私有元素声明之后定义。
  • 规范和正文中引用的数据库对象在包编译时必须处于有效状态。

包装信息

包创建完成后,包信息(例如包源、子程序详细信息和重载详细信息)即可在以下位置找到: Oracle 数据字典表。

下表给出了数据字典表以及每个表中可用的包信息。

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

UTL_FILE – 概述

UTL_FILE 是由 UTL_FILE 提供的独立实用程序包。 Oracle 它用于执行特殊任务。主要用于从 PL/SQL 包或子程序中读取和写入操作系统文件。它具有用于向文件中写入信息和从文件中读取信息的独立函数。它还允许使用本机字符集进行读写操作。

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

常见问题

包提供了模块化、信息隐藏和更简便的应用程序设计。规范公开了一个公共接口,而规范主体则隐藏了具体的实现细节。 Oracle 在第一次调用时将程序包加载到内存中,这样后续的子程序调用就可以避免磁盘 I/O,运行速度更快。

一个软件包包含许多相关内容 子程序 并且,共享对象使用同一个名称,从而将公共规范与私有主体分离。独立过程或函数是一个单独的、独立的模式对象,不存在这种接口与实现分离的情况。

是的,如果规范仅声明变量、常量、类型或异常。一旦规范声明任何过程、函数或游标,则必须包含主体,因为主体必须提供它们的实现。否则,规范可以独立存在。

使用 DROP PACKAGE 命令可以同时删除包规范和包体,使用 DROP PACKAGE BODY 命令可以仅删除包体。如果包无效,可以使用 ALTER PACKAGE package_name COMPILE 命令重新编译,或者使用 COMPILE BODY 命令在更改后仅重新编译包体。

%ROWTYPE 属性声明了一个 记录 其字段与 emp 表的列相匹配。使用 emp%ROWTYPE 可以确保 get_record 和 set_record 参数与表结构保持一致,因此列更改所需的代码修改更少。

是的。每个引用包的会话都会获得自己的实例和包状态,该状态在该会话的生命周期内保持不变。重新编译请求体会丢弃该状态,并在下次调用时引发 ORA-04068 错误。

是的。 GitHub 副驾驶 根据注释草拟包规范、匹配主体和重载子程序,并建议 %ROWTYPE 参数。 Rev部署前请查看生成的边界、初始化块和异常处理。

AI助手会扫描软件包,查找无效状态、缺失的主体定义以及规范变更时会断开的依赖链。这种机器学习审查会在软件包投入生产环境之前,标记出含义模糊的重载子程序和重新编译的风险。

总结一下这篇文章: