Oracle PL/SQL 包设计与最佳实践
Oracle PL/SQL 包设计与最佳实践
适用版本:Oracle Database 10g / 11g / 12c / 19c / 23ai 文档版本:v1.0 / 2026-07
1. 概述
PL/SQL 包是模块化设计核心[1]:
详细见:Oracle PL/SQL 包详解。
2. 包结构
2.1 规范
CREATE OR REPLACE PACKAGE emp_pkg AS
-- 类型
TYPE emp_rec IS RECORD (
id employees.id%TYPE,
name employees.name%TYPE,
salary employees.salary%TYPE
);
-- 异常
e_emp_not_found EXCEPTION;
PRAGMA EXCEPTION_INIT(e_emp_not_found, -20001);
-- 常量
c_max_salary CONSTANT NUMBER := 100000;
-- 游标
CURSOR c_emp(p_dept_id NUMBER) IS
SELECT * FROM employees WHERE dept_id = p_dept_id;
-- 公共变量
v_session_user VARCHAR2(30);
-- 过程
PROCEDURE hire_emp(
p_name IN VARCHAR2,
p_salary IN NUMBER,
p_dept_id IN NUMBER
);
-- 函数
FUNCTION get_emp_count(p_dept_id NUMBER) RETURN NUMBER;
END emp_pkg;
/
2.2 主体
CREATE OR REPLACE PACKAGE BODY emp_pkg AS
-- 私有变量
v_total_hired NUMBER := 0;
-- 私有函数
FUNCTION validate_salary(p_salary NUMBER) RETURN BOOLEAN IS
BEGIN
RETURN p_salary > 0 AND p_salary <= c_max_salary;
END;
-- 公共过程
PROCEDURE hire_emp(
p_name IN VARCHAR2,
p_salary IN NUMBER,
p_dept_id IN NUMBER
) IS
v_id NUMBER;
BEGIN
IF NOT validate_salary(p_salary) THEN
RAISE_APPLICATION_ERROR(-20002, 'Invalid salary');
END IF;
SELECT emp_seq.NEXTVAL INTO v_id FROM dual;
INSERT INTO employees (id, name, salary, dept_id)
VALUES (v_id, p_name, p_salary, p_dept_id);
v_total_hired := v_total_hired + 1;
END;
-- 公共函数
FUNCTION get_emp_count(p_dept_id NUMBER) RETURN NUMBER IS
v_count NUMBER;
BEGIN
SELECT COUNT(*) INTO v_count FROM employees WHERE dept_id = p_dept_id;
RETURN v_count;
END;
-- 初始化(可选)
BEGIN
v_session_user := USER;
END emp_pkg;
/
3. 重载
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;
/
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;
/
4. 状态管理
4.1 会话状态
CREATE OR REPLACE PACKAGE session_pkg AS
v_user_id NUMBER;
v_login_time TIMESTAMP;
PROCEDURE init(p_user_id NUMBER);
END;
/
CREATE OR REPLACE PACKAGE BODY session_pkg AS
PROCEDURE init(p_user_id NUMBER) IS
BEGIN
v_user_id := p_user_id;
v_login_time := SYSTIMESTAMP;
END;
END;
/
4.2 SERIALLY_REUSABLE
CREATE OR REPLACE PACKAGE temp_pkg AS
PRAGMA SERIALLY_REUSABLE;
v_counter NUMBER := 0;
PROCEDURE increment;
END;
/
-- 状态不跨调用保留
5. 包变量持久化
5.1 限制
- 会话级
- 重启丢失
- RAC 各节点独立
5.2 替代
- 表
- Global Temporary
- Context
6. PRAGMA
6.1 AUTONOMOUS_TRANSACTION
PROCEDURE log_msg(p_msg VARCHAR2) IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO log VALUES (p_msg, SYSTIMESTAMP);
COMMIT;
END;
6.2 SERIALLY_REUSABLE
PRAGMA SERIALLY_REUSABLE;
-- 内存优化
7. 初始化块
CREATE OR REPLACE PACKAGE BODY pkg AS
...
BEGIN
-- 首次引用时执行
-- 一次/会话
v_init_time := SYSDATE;
END pkg;
/
8. RESULT CACHE
8.1 函数
CREATE OR REPLACE PACKAGE lookup_pkg AS
FUNCTION get_dept_name(p_id NUMBER) RETURN VARCHAR2
RESULT_CACHE RELIES_ON (departments);
END;
/
CREATE OR REPLACE PACKAGE BODY lookup_pkg AS
FUNCTION get_dept_name(p_id NUMBER) RETURN VARCHAR2
RESULT_CACHE RELIES_ON (departments)
IS
v_name VARCHAR2(100);
BEGIN
SELECT dept_name INTO v_name FROM departments WHERE id = p_id;
RETURN v_name;
END;
END;
/
详细见:Oracle PL/SQL 性能优化详解。
9. ACCESSIBLE BY(12c+)
CREATE OR REPLACE PACKAGE emp_pkg
ACCESSIBLE BY (PROCEDURE hr_proc, FUNCTION hr_fn)
AS
...
END;
-- 仅指定单元可访问
10. 包权限
10.1 执行
GRANT EXECUTE ON emp_pkg TO hr_role;
10.2 AUTHID
CREATE OR REPLACE PACKAGE emp_pkg
AUTHID DEFINER -- 默认
AS ...
CREATE OR REPLACE PACKAGE emp_pkg
AUTHID CURRENT_USER -- 调用者
AS ...
详细见:Oracle 存储过程与函数详解。
11. 编译
11.1 重新编译
ALTER PACKAGE emp_pkg COMPILE;
ALTER PACKAGE emp_pkg COMPILE SPECIFICATION;
ALTER PACKAGE emp_pkg COMPILE BODY;
11.2 错误
SHOW ERRORS PACKAGE emp_pkg;
SHOW ERRORS PACKAGE BODY emp_pkg;
SELECT name, type, line, position, text
FROM user_errors
WHERE name = 'EMP_PKG';
12. 查看
12.1 源代码
SELECT name, type, line, text
FROM user_source
WHERE name = 'EMP_PKG'
ORDER BY line;
12.2 对象
SELECT object_name, object_type, status
FROM user_objects
WHERE object_name = 'EMP_PKG';
12.3 依赖
SELECT name, type, referenced_name, referenced_type
FROM user_dependencies
WHERE name = 'EMP_PKG';
13. 包设计原则
13.1 高内聚
- 相关功能
- 业务模块
- 一致
13.2 低耦合
- 最小依赖
- 接口清晰
- 独立
13.3 接口稳定
- 规范少改
- 主体可变
- 兼容
13.4 命名
- pkg_xxx
- 业务前缀
- 清晰
14. 常见包
14.1 DBMS_OUTPUT
DBMS_OUTPUT.PUT_LINE(...);
DBMS_OUTPUT.ENABLE;
14.2 DBMS_SQL
-- 动态 SQL
14.3 UTL_FILE
-- 文件操作
14.4 DBMS_LOB
-- LOB 操作
14.5 DBMS_STATS
-- 统计
14.6 DBMS_SCHEDULER
-- 调度
15. 常见坑与排错
15.1 状态丢失
- 重启
- RAC
- SERIALLY_REUSABLE
15.2 失效
- 依赖变化
- 重新编译
- INVALID
15.3 循环依赖
- A 依赖 B
- B 依赖 A
- 重构
16. 最佳实践
- 模块化:高内聚
- 接口稳定:规范
- 重载:灵活
- 私有:隐藏
- 常量:定义
- 异常:集中
- RESULT_CACHE:性能
- AUTHID:权限
- 初始化:默认
- 文档化:注释
17. 参考资料
[1] Oracle Database PL/SQL Language Reference 19c, “Packages” https://docs.oracle.com/en/database/oracle/oracle-database/19/lnpls/plsql-packages.html