CREATE PACKAGE

CREATE PACKAGE定义一个包。CREATE OR REPLACE PACKAGE将会创建一个新包或者替换一个已有的定义。包是组织和封装数据库应用程序逻辑的重要工具。通过合理设计和使用包,可以提高代码的可维护性、重用性和性能。

简介

CREATE PACKAGE语句用于创建存储包的规范,存储包是存储在数据库中的相关过程、函数及其他程序对象的封装集合。包规范声明了这些对象,而随后的包体则定义了这些对象的具体实现。

如果创建包时包含schema名称,则将在指定的schema中创建。否则,将在当前schema中创建。除非要更新现有包的定义,否则新包的名称不能与同一schema中的任何现有包匹配。在这种情况下,请使用CREATE OR REPLACE PACKAGE。创建该过程的用户将成为包的所有者。

包是将过程和函数创建为独立模块对象的替代方案。

语法

CREATE [ OR REPLACE ] PACKAGE <package_name>
    [ AUTHID { DEFINER | CURRENT_USER } ]
    { IS | AS }
    [ <declaration>; ] [, ...]
[   {
        PROCEDURE <proc_name>
        [ (<argname> [ IN | IN OUT | OUT ] <argtype> [ DEFAULT <value> ]
        [, ...]) ];
        [ PRAGMA RESTRICT_REFERENCES(<proc_name>,
        { RNDS | RNPS | TRUST | WNDS | WNPS } [, ... ] ); ]
        |
        FUNCTION <func_name>
        [ (<argname> [ IN | IN OUT | OUT ] <argtype> [ DEFAULT <value> ]
        [, ...]) ]
        RETURN <rettype> [ DETERMINISTIC ];
        [ PRAGMA RESTRICT_REFERENCES(<func_name>,
        { RNDS | RNPS | TRUST | WNDS | WNPS } [, ... ] ); ]
    }
] [, ...]
END [ <name> ]

参数说明

参数

描述

package_name

要创建的包的名称(可选schema限定)。

DEFINER | CURRENT_USER

指定使用何种权限访问包中引用的数据库对象。取值范围如下:

  • DEFINER(默认值):使用包所有者权限访问包中引用的数据库对象。

  • CURRENT_USER:使用执行包的当前用户的权限访问包中引用的数据库对象。

declaration

公共变量、类型、游标或REF CURSOR声明。

proc_name

公共存储过程名称。

argname

参数名称。

IN | IN OUT | OUT

参数模式。

argtype

程序参数的数据类型。

DEFAULT value

输入参数的默认值。

func_name

公共函数名称。

rettype

返回数据类型。

DEFERMININTIC

DETERMINISTICIMMUTABLE的同义词。DETERMINISTIC过程不能修改数据库,并且在给定相同的参数值时总是达到相同的结果。它不执行数据库查找,只使用其参数列表中直接存在的信息。如果包含此子句,则可以立即用过程值的所有常量参数替换过程的任何调用。

RNDS | RNPS | TRUST | WNDS | WNPS

为实现兼容性提供该参数,请忽略。

结构

包头定义了包的接口部分,包括公开的常量、变量、类型、过程和函数等。其他程序单元可以引用包头中声明的对象。

CREATE OR REPLACE PACKAGE Employee_Pkg AS
    -- 常量
    MAX_SALARY CONSTANT NUMBER := 100000;

    -- 过程声明
    PROCEDURE Add_Employee(
        p_emp_id     IN NUMBER,
        p_first_name IN VARCHAR2,
        p_last_name  IN VARCHAR2,
        p_salary     IN NUMBER
    );

    PROCEDURE Update_Salary(
        p_emp_id     IN NUMBER,
        p_new_salary IN NUMBER
    );

    -- 函数声明
    FUNCTION Get_Employee_Name(
        p_emp_id IN NUMBER
    ) RETURN VARCHAR2;
END Employee_Pkg;

注意事项

  • 依赖管理:修改包头会导致依赖包的所有对象需要重新编译,请谨慎进行变更。

  • 错误处理:在包中妥善处理异常,避免未捕获的异常导致程序中断。

  • 性能优化:合理使用包的私有变量和缓存机制,提高性能。

  • 安全性:通过包的权限控制限制对敏感数据和功能的访问。

  • 命名规范:遵循统一的命名规范,便于代码理解和维护。

使用说明

一旦包成功创建并编译完成,即可在PL/SQL代码中引用包内的过程、函数等。

调用包中的过程和函数

  • 调用过程。

    BEGIN
        Employee_Pkg.Add_Employee(
            p_emp_id     => 1001,
            p_first_name => '张',
            p_last_name  => '三',
            p_salary     => 50000
        );
    END;
  • 调用函数

    DECLARE
        v_emp_name VARCHAR2(100);
    BEGIN
        v_emp_name := Employee_Pkg.Get_Employee_Name(1001);
        DBMS_OUTPUT.PUT_LINE('员工姓名: ' || v_emp_name);
    END;

包同名函数处理

Oracle支持包中的同名函数,实现类似多态的行为,对应的PolarDB也支持相同的行为。

说明

PolarDB PostgreSQL版Oracle语法兼容 2.0在内核小版本2.0.14.26.0新增创建IN参数相同,OUT参数不同的同名函数。若集群的内核小版本低于该版本且需要使用该功能,请将内核小版本升级到2.0.14.26.0或以上版本。查询和升级内核小版本,请参见版本管理

CREATE OR REPLACE PACKAGE Employee_Pkg AS
    -- 常量
    MAX_SALARY CONSTANT NUMBER := 100000;

    -- 函数声明
    FUNCTION Get_Employee_Name(
        p_emp_id IN NUMBER
    ) RETURN VARCHAR2;

    -- 同名函数
    FUNCTION Get_Employee_Name(
        p_emp_id IN NUMBER,
        res_info OUT text
    ) RETURN VARCHAR2;
END Employee_Pkg;

包函数使用包变量作为参数默认值

说明

PolarDB PostgreSQL版Oracle语法兼容 2.0在内核小版本2.0.14.26.0新增使用包变量做函数参数默认值。若集群的内核小版本低于该版本且需要使用该功能,请将内核小版本升级到2.0.14.26.0或以上版本。查询和升级内核小版本,请参见版本管理

CREATE OR REPLACE PACKAGE test_package AS
    a NUMBER := 1;
    b NUMBER := 2;
END my_package;

CREATE OR REPLACE PACKAGE my_package AS
    FUNCTION add_numbers(x NUMBER default test_package.a, y NUMBER default test_package.b) RETURN NUMBER;
    PROCEDURE print_message(msg VARCHAR2);
END my_package;

维护和更新包

说明

如需向包中添加新的公开对象,必须对包头和包体进行相应的修改。修改包头将导致依赖该包的所有对象失效,需要重新进行编译。如果仅对包体中的实现进行修改(不影响包头),则无需重新编译依赖对象。详细的包体SQL语句介绍请参考CREATE PACKAGE BODY

向包中添加新函数。

  1. 修改包头。

    CREATE OR REPLACE PACKAGE Employee_Pkg AS
       -- 现有声明
       FUNCTION Get_Employee_Department(p_emp_id IN NUMBER) RETURN VARCHAR2;
    END Employee_Pkg;
  2. 修改包体。

    CREATE OR REPLACE PACKAGE BODY Employee_Pkg AS
       -- 现有实现
    
       FUNCTION Get_Employee_Department(p_emp_id IN NUMBER) RETURN VARCHAR2 IS
           v_department VARCHAR2(100);
       BEGIN
           SELECT department
           INTO v_department
           FROM employees
           WHERE emp_id = p_emp_id;
           RETURN v_department;
       EXCEPTION
           WHEN NO_DATA_FOUND THEN
               RETURN 'Not Assigned';
       END Get_Employee_Department;
    END Employee_Pkg;

相关参考

完整的包创建和使用示例请参考示例