Skip to content

适用虚谷数据库版本

v12.9



适用虚谷数据库版本

v12.9


📄字数 4.7K
👁️阅读量 加载中...

包(Package)是指一组逻辑相关的 PL/SQL 程序单元(如存储过程、函数、变量、常量、游标等)的集合,通常用于模块化管理、封装业务逻辑、提升可维护性和性能,属于 PL/SQL 编程语言体系的核心组件之一。

一、创建包

一个包对象结构包括:包规范(包头)与包体。

包头定义了包对象所有的公有元素信息,可包括游标、数据类型、存储过程、函数等元素。

包体是对包头定义的具体实现,在包体内定义公有存储过程或函数的执行体。同时包体内还可以声明包的私有元素,如果声明的游标、变量、用户类型等在包头内未定义,则这些对象为私有对象,用户不可通过包进行访问,但可在包体内进行调用。

1.1 包头定义

1.1.1 语法格式

1.1.2 参数说明

  • FORCE: 强制创建包,即使依赖对象无效。
  • OR REPLACE: 替换现有包。
  • schema_name:模式名,缺省值为当前模式。
  • package_name:包名。
  • DEFAULT: 默认权限,Opt_Authid缺省时,也使用该权限。
  • DEFINER:使用定义者的权限。
  • CURRENT_USER | USER: 使用调用者的权限。
  • COMMENT SCONST:注释。
  • PackSpecTtem:包体声明。
    • 使用ISAS关键字开始声明包体。
    • 可以包含一个或多个PackSpecItem(变量或存储过程声明),每个项以分号;结尾。
  • END:结束标记。
  • VarDef:变量定义,可参考declare文档中关于VarDef的介绍。
  • ProcDecl:存储过程声明,可参考procedure文档中关于ProcDecl的介绍。
  • FuncDecl:函数声明,可参考funtion文档中关于FuncDecl的介绍

提示

在包头定义中,针对存储过程与函数的定义可通过重载的方式实现不同输入参数的同名存储过程/函数声明。

1.1.3 示例

sql
-- 强制创建包,即使存在错误
SQL> CREATE FORCE package pkg_force
     IS
         FUNCTION func_err_type(var1 INT_ERR) RETURN INT;
     END;
     /

Execute successful.

Warning: [E8007] 编译包体PKG_FORCE失败
        [E17002] 数据类型INT_ERR不存在

Use time:10 ms.

SQL> CREATE package pkg_def
     COMMENT 'This is a package'
     IS
         var1 INT;
         var2 VARCHAR := 'test string';
         PROCEDURE proc_def(var3 INT);
         FUNCTION func_def(var4 VARCHAR) RETURN VARCHAR;
     END pkg_def;
     /

Execute successful.
Use time:4 ms.

1.2 包体创建

1.2.1 语法格式

1.2.2 参数说明

  • FORCE:强制创建包体,即使依赖对象无效。
  • OR REPLACE:替换现有包体。
  • schema_name:模式名,缺省值为当前模式。
  • package_name:包名。
  • DECLARE VarDefList:声明部分,DECLARE关键字可选,标准PL/SQL包体中通常省略,变量直接定义在IS|AS后。
  • VarDefList:变量定义列表,可参考declare文档中关于VarDefList的介绍。
  • PackMemberDef:存储过程或函数定义,可参考procedure文档中关于ProcDeffunction文档中关于FuncDef的介绍。
  • BEGIN pl_stmt_list:初始化块,用于执行包初始化逻辑(如赋值、调用存储过程),在包第一次被调用时执行,后续调用不再执行。pl_stmt_listPL/SQL语句列表,可参考SQL执行文档
  • EXCEPTION:异常处理,捕获包级异常。Exception_Item可参考异常处理
  • END:结束标记,END后可选跟包名,当前面"PACKAGE BODY"后衔接的是schema_name.package_name格式时,END后的包名可以是完整的schema_name.package_name,也可以仅是package_name

提示

在包体内声明与定义的对象在包头内若无声明,则对象为私有元素对象,这类元素对象只能在包体内调用,用户无法通过包进行访问使用。

1.2.3 示例

sql
-- 创建包头
SQL> CREATE OR REPLACE FORCE PACKAGE PKG_EXCEPTION_TEST
     IS
         FUNCTION func_test() RETURN INT;
     END;
     /

Execute successful.
Use time:5 ms.

-- 创建有EXCEPTION处理的包体
SQL> CREATE OR REPLACE FORCE PACKAGE BODY PKG_EXCEPTION_TEST
     IS
         FUNCTION func_test() RETURN INT
         IS
             var_out INT;
         BEGIN
             SELECT COUNT(*) INTO var_out FROM
             TAB_PKG_TEST1;
             RETURN var_out;
         END;
     EXCEPTION
         WHEN OTHERS THEN
             DBMS_OUTPUT.PUT_LINE('Error: ' || SQLERRM);
     END;
     /

Execute successful.
Use time:11 ms.

-- 创建包头:
SQL> CREATE OR REPLACE PACKAGE pkg_test
     AUTHID DEFAULT
     COMMENT '输出USER_TABLES视图中表名'
     IS
         PROCEDURE proc1();
     END;
     /

Execute successful.
Use time:0 ms.

-- 创建包体:
SQL> CREATE OR REPLACE PACKAGE body pkg_test IS
         PROCEDURE proc1() IS
             CURSOR cur IS
             SELECT table_name FROM user_tables;
         con  INTEGER;
         name VARCHAR;
         BEGIN
             SELECT count(*) INTO con FROM user_tables;
             OPEN cur;
             FOR i IN 1 .. con LOOP
                 FETCH cur
                     INTO name;
                 send_msg(name);
             END LOOP;
             close cur;
         END;
     END;
     /

Execute successful.
Use time:1 ms.

二、使用包

2.1 使用方式

包头中声明的对象均使用.符号来调用,如package_name.subprogram_name。包对象内的成员对象访问与普通对象访问类似,使用包仅仅起到封装作用。同时经过包封装,应用程序在调用包时即编译包对象,并将对象信息加载至缓存,后续调用无需再次编译以提高使用性能。

2.2 示例

sql
-- 创建table,使user_tables表中存在记录
SQL> CREATE TABLE tab_pkg_test(var1 INT);

Execute successful.
Use time:26 ms.

SQL> BEGIN
         -- 调用 pkg_test 包中的 proc1 存储过程
         pkg_test.proc1;
     EXCEPTION
         WHEN OTHERS THEN
             -- 处理可能发生的异常
             DBMS_OUTPUT.PUT_LINE('Error calling pkg_test.proc1:' || SQLERRM);
     END;
     /
TAB_PKG_TEST

Execute successful.
Use time:2 ms.

也可以直接使用EXEC命令,是BEGIN ... END;块的一种简化形式。

sql
SQL> EXEC pkg_test.proc1;
TAB_PKG_TEST

Execute successful.
Use time:0 ms.

三、重编译失效包

3.1 包的失效

包的失效与重编译是非常常见的问题,尤其是在系统升级或依赖对象变化时。失效的原因,可能是依赖对象变化、依赖对象无效、包含的对象被手动修改或删除等。

3.2 包状态查看

可以通过SYS_PACKAGES系统表的VALID字段确认包是否失效。

3.2.1 示例

sql
SQL> select PACK_NAME, VALID from SYS_PACKAGES where PACK_NAME = 'PKG_TEST';

+-----------+-------+
| PACK_NAME | VALID |
+-----------+-------+
| PKG_TEST  | T     |
+-----------+-------+

(1 row)
Use time:0 ms.

3.3 包的重编译

在包失效后,可以通过手动的方式对包进行重编译,使其变为有效状态。

3.3.1 语法格式

3.3.2 示例

sql
-- 构造失效包
SQL> CREATE FORCE PACKAGE PKG_INVALID_TEST
     IS
         FUNCTION func_test() RETURN INT;
     END;
     /

Execute successful.
Use time:5 ms.

-- 表TAB_PKG_TEST1不存在
SQL> CREATE OR REPLACE FORCE PACKAGE BODY PKG_INVALID_TEST
     IS
         FUNCTION func_test() RETURN INT
         IS
             var_out INT;
         BEGIN
             SELECT COUNT(*) INTO var_out FROM TAB_PKG_TEST1;
             RETURN var_out;
         END;
     END;
     /

Execute successful.

Warning: [E8007] 编译包体PKG_INVALID_TEST失败
        [E19162 L7 C36] 表或视图TAB_PKG_TEST1不存在

Use time:2 ms.

-- 查看PACKAGE PKG_INVALID_TEST状态
SQL> select PACK_NAME, VALID from SYS_PACKAGES where PACK_NAME = 'PKG_INVALID_TEST';

+------------------+-------+
|    PACK_NAME     | VALID |
+------------------+-------+
| PKG_INVALID_TEST | F     |
+------------------+-------+

(1 row)
Use time:0 ms.

-- 创建依赖的表
SQL> CREATE TABLE TAB_PKG_TEST1(var INT);

Execute successful.
Use time:29 ms.

-- 执行失效包重编译
SQL> ALTER PACKAGE PKG_INVALID_TEST RECOMPILE;

Execute successful.
Use time:1 ms.

-- 再次查看包状态
SQL> select PACK_NAME, VALID from SYS_PACKAGES where PACK_NAME = 'PKG_INVALID_TEST';

+------------------+-------+
|    PACK_NAME     | VALID |
+------------------+-------+
| PKG_INVALID_TEST | T     |
+------------------+-------+

(1 row)
Use time:0 ms.

四、删除包

通过DROP语句删除包,包头与包体会被一并删除。

4.1 语法格式

4.2 参数说明

  • CASCADE | RESTRICT:可选项,默认使用RESTRICT,在删除存储过程时会检测存储过程是否被其他对象依赖,若存在依赖则删除失败,若无依赖则删除成功;CASCADE则表示强制删除存储过程,强制删除会无视依赖关系删除掉依赖对象。

4.3 示例

sql
SQL> DROP PACKAGE PKG_INVALID_TEST;

Execute successful.
Use time:0 ms.