Oracle PL/SQL 包(Package)设计
Oracle PL/SQL 包(Package)设计
适用版本:Oracle Database 8i / 9i / 10g / 11g / 12c / 19c / 23ai 文档版本:v1.0 / 2026-07
1. 概述
PL/SQL 包(Package) 是相关对象(过程/函数/类型/变量)的集合[1]:
优势:
- 模块化
- 封装
- 性能(首次加载后驻留)
- 重载
- 状态保持
2. 包结构
2.1 包规范(Specification)
CREATE OR REPLACE PACKAGE emp_pkg AS
-- 公共类型
TYPE emp_record IS RECORD (
id NUMBER,
name VARCHAR2(100),
salary NUMBER
);
-- 公共变量
g_max_salary CONSTANT NUMBER := 100000;
g_dept_id NUMBER;
-- 公共异常
e_invalid_salary EXCEPTION;
PRAGMA EXCEPTION_INIT(e_invalid_salary, -20001);
-- 公共游标
CURSOR c_emp RETURN emp_record;
-- 公共过程
PROCEDURE update_salary(
p_emp_id NUMBER,
p_salary NUMBER
);
-- 公共函数
FUNCTION get_total_salary(
p_dept_id NUMBER
) RETURN NUMBER;
END emp_pkg;
/
2.2 包体(Body)
CREATE OR REPLACE PACKAGE BODY emp_pkg AS
-- 私有变量
v_count NUMBER;
-- 私有函数
FUNCTION validate_salary(p_salary NUMBER) RETURN BOOLEAN IS
BEGIN
RETURN p_salary > 0 AND p_salary <= g_max_salary;
END;
-- 游标实现
CURSOR c_emp RETURN emp_record IS
SELECT employee_id, last_name, salary FROM employees;
-- 过程实现
PROCEDURE update_salary(
p_emp_id NUMBER,
p_salary NUMBER
) AS
BEGIN
IF NOT validate_salary(p_salary) THEN
RAISE e_invalid_salary;
END IF;
UPDATE employees
SET salary = p_salary
WHERE employee_id = p_emp_id;
END;
-- 函数实现
FUNCTION get_total_salary(
p_dept_id NUMBER
) RETURN NUMBER AS
v_total NUMBER;
BEGIN
SELECT SUM(salary) INTO v_total
FROM employees WHERE dept_id = p_dept_id;
RETURN v_total;
END;
BEGIN
-- 初始化部分(首次调用执行)
v_count := 0;
g_dept_id := 10;
END emp_pkg;
/
3. 重载(Overloading)
3.1 同名不同参数
CREATE OR REPLACE PACKAGE calc_pkg AS
FUNCTION add(a NUMBER, b NUMBER) RETURN NUMBER;
FUNCTION add(a VARCHAR2, b VARCHAR2) RETURN VARCHAR2;
FUNCTION add(a DATE, b NUMBER) RETURN DATE;
END calc_pkg;
/
CREATE OR REPLACE PACKAGE BODY calc_pkg AS
FUNCTION add(a NUMBER, b NUMBER) RETURN NUMBER IS
BEGIN
RETURN a + b;
END;
FUNCTION add(a VARCHAR2, b VARCHAR2) RETURN VARCHAR2 IS
BEGIN
RETURN a || b;
END;
FUNCTION add(a DATE, b NUMBER) RETURN DATE IS
BEGIN
RETURN a + b;
END;
END calc_pkg;
/
-- 调用
SELECT calc_pkg.add(1, 2) FROM dual;
SELECT calc_pkg.add('Hello', ' World') FROM dual;
SELECT calc_pkg.add(SYSDATE, 7) FROM dual;
4. 包状态
4.1 会话级状态
CREATE OR REPLACE PACKAGE state_pkg AS
v_counter NUMBER := 0;
PROCEDURE increment;
PROCEDURE reset;
END state_pkg;
/
CREATE OR REPLACE PACKAGE BODY state_pkg AS
PROCEDURE increment IS
BEGIN
v_counter := v_counter + 1;
END;
PROCEDURE reset IS
BEGIN
v_counter := 0;
END;
END state_pkg;
/
-- 会话 1
EXEC state_pkg.increment;
EXEC state_pkg.increment;
-- v_counter = 2
-- 会话 2(独立)
EXEC state_pkg.increment;
-- v_counter = 1
4.2 PRAGMA SERIALLY_REUSABLE
-- 状态不保持(每次调用重置)
CREATE OR REPLACE PACKAGE temp_pkg AS
PRAGMA SERIALLY_REUSABLE;
v_counter NUMBER := 0;
END temp_pkg;
/
5. 包初始化
5.1 初始化部分
CREATE OR REPLACE PACKAGE BODY init_pkg AS
...
BEGIN
-- 首次调用时执行
DBMS_OUTPUT.PUT_LINE('Package initialized');
v_count := 0;
SELECT COUNT(*) INTO v_count FROM employees;
EXCEPTION
WHEN OTHERS THEN
NULL;
END init_pkg;
/
6. 包依赖
6.1 包依赖图
emp_pkg → employees 表
→ dept_pkg
→ departments 表
6.2 重编译
-- 编译包
ALTER PACKAGE emp_pkg COMPILE;
ALTER PACKAGE emp_pkg COMPILE SPECIFICATION;
ALTER PACKAGE emp_pkg COMPILE BODY;
6.3 依赖管理
-- 查看依赖
SELECT name, type, referenced_name, referenced_type
FROM user_dependencies
WHERE name = 'EMP_PKG';
7. 包权限
7.1 授予执行权限
GRANT EXECUTE ON emp_pkg TO hr_user;
GRANT EXECUTE ON emp_pkg TO PUBLIC;
7.2 同义词
CREATE PUBLIC SYNONYM emp_pkg FOR scott.emp_pkg;
8. 包设计原则
8.1 高内聚
- 相关功能放一起
- 单一职责
- 清晰命名
8.2 低耦合
- 最小化公共接口
- 私有实现细节
- 接口稳定
8.3 命名规范
包名:emp_pkg
过程:update_salary
函数:get_total_salary
变量:v_count / g_dept_id
常量:c_max
异常:e_invalid
9. 常用包
9.1 DBMS_OUTPUT
EXEC DBMS_OUTPUT.ENABLE;
EXEC DBMS_OUTPUT.PUT_LINE('Hello');
9.2 DBMS_SQL
-- 动态 SQL
9.3 DBMS_LOB
-- LOB 操作
9.4 DBMS_STATS
-- 统计信息
EXEC DBMS_STATS.GATHER_TABLE_STATS('SCOTT', 'EMPLOYEES');
9.5 UTL_FILE
-- 文件操作
10. 常见坑与排错
10.1 ORA-06508: 包不存在
-- 1. 检查包是否存在
SELECT * FROM user_objects WHERE object_type='PACKAGE';
-- 2. 编译
ALTER PACKAGE emp_pkg COMPILE;
10.2 ORA-04068: 包状态丢弃
-- 包被重编译后会话状态失效
-- 重新调用即可
10.3 状态丢失
原因:包被重编译或数据库重启。
修复:
-- 使用 PRAGMA SERIALLY_REUSABLE(如适合)
-- 或持久化状态
10.4 编译错误
-- 查看错误
SHOW ERRORS PACKAGE emp_pkg;
SHOW ERRORS PACKAGE BODY emp_pkg;
-- 或
SELECT * FROM user_errors WHERE name='EMP_PKG';
11. 最佳实践
- 业务逻辑封装到包:模块化
- 规范/体分离:接口清晰
- 公共/私有分明:封装
- 合理重载:灵活
- 状态谨慎使用:可能丢失
- 包初始化轻量:避免启动慢
- 依赖最小化:降低耦合
- 异常处理:健壮性
- 定期重编译:避免失效
- 文档完整:易维护
12. 参考资料
[1] Oracle Database PL/SQL Language Reference 19c, “PL/SQL Packages” https://docs.oracle.com/en/database/oracle/oracle-database/19/lnpls/plsql-packages.html