必须先创建包规范(PACKAGE SPEC),否则包体编译报错;规范声明接口(函数、过程、常量、游标类型),包体实现逻辑并可含私有成员,调用须带包名前缀,修改规范会导致依赖对象失效。
包规范(PACKAGE SPEC)必须先创建,否则包体编译报错
pl/sql 包分两部分:先写 package 规范(声明接口),再写 package body(实现逻辑)。如果跳过规范直接建包体,oracle 会报 pls-00923: native compilation not supported for this platform 这类误导性错误——其实真正原因是找不到对应规范。
实操建议:
- 始终用
CREATE OR REPLACE PACKAGE pkg_name AS ... END;先建规范,哪怕只声明一个空过程 - 规范里只放需要对外暴露的内容:
FUNCTION、PROCEDURE、公共常量(g_debug CONSTANT BOOLEAN := TRUE;)、游标类型(TYPE t_cur IS REF CURSOR;) - 不要在规范里写变量赋值语句或执行逻辑,否则编译失败
- 规范中函数/过程的参数名可以和包体里不一致,但类型、顺序、个数必须严格一致
包体(PACKAGE BODY)里能写私有子程序,但调用顺序有约束
包体里可以定义规范里没声明的 FUNCTION 或 PROCEDURE,这些就是私有成员,外部无法调用。但 PL/SQL 不支持“前向声明”,所以如果私有函数 A 调用了私有函数 B,B 必须出现在 A 之前,否则报 PLS-00302: component 'B' must be declared。
实操建议:
- 把被依赖的私有函数/过程放在前面,主入口逻辑(如公开的
process_order)放在后面 - 不想手动排顺序?统一用
DECLARE ... BEGIN ... END;块封装私有逻辑,或者改用嵌套子程序(但仅限于当前执行块内) - 包体里可以访问规范中声明的公共变量,但不能重新声明同名变量(会遮蔽,且 Oracle 可能不报错但行为异常)
- 包级变量(在包体
BEGIN前声明的变量)是会话级持久的,别默认当成“每次调用都重置”
调用包内过程或函数时,必须加包名前缀
即使当前在同一个包体内,调用本包的公开函数也必须写 pkg_name.func_name;不加前缀会被当作本地调用,若本地没定义就报 PLS-00201: identifier 'func_name' must be declared。这是 PL/SQL 和其他语言很不同的地方。
实操建议:
- 所有对包内公开成员的引用,包括包体内自调用,一律用
pkg_name.member_name - 包名大小写敏感:如果用双引号创建了小写包名(
"my_pkg"),调用时也必须加引号,否则找不到 - SQL*Plus 或 SQL Developer 中执行包内函数,要用
SELECT pkg_name.func_name(...) FROM DUAL;,不能直接写函数名 - 从 Java 或 Python 调用时,注意 JDBC 驱动对包过程的调用语法(比如需用
{call pkg_name.proc_name(?)})
修改包规范后,所有依赖它的包体都会失效
Oracle 把包规范看作接口契约。一旦你改了规范里的函数签名(比如增删参数、改类型),所有引用该包的包体、视图、甚至存储过程都会变成 INVALID 状态,下次调用时才触发隐式重编译——可能失败,也可能引入意料外的行为。
实操建议:
- 上线前查依赖:
SELECT * FROM ALL_DEPENDENCIES WHERE REFERENCED_NAME = 'PKG_NAME' AND REFERENCED_TYPE = 'PACKAGE'; - 加新功能优先用新增函数/过程,而不是修改旧接口;兼容性比“整洁”更重要
- 如果必须改签名,记得同步更新所有调用方,并在变更脚本里显式
ALTER PACKAGE ... COMPILE BODY检查是否通过 - 包级变量初始化代码(包体开头的
BEGIN ... END;块)只在首次加载时运行一次,重启会话或ALTER SYSTEM FLUSH SHARED_POOL才会重跑——这点常被忽略
包的模块化价值不在“写起来多优雅”,而在接口收敛和变更可控。真正麻烦的不是语法,是团队里有人绕过规范直接改包体、或者用包级变量存状态还假设它线程安全。










