包规范(spec)只声明接口,不包含实现代码,如function get_salary(emp_id number) return number;;包体(body)负责实现并可含私有内容,调用时须带包名前缀。

包规范(SPEC)只声明,不实现
包规范是对外暴露的“接口”,类似 Java 的 interface。它只做声明:哪些变量、常量、游标、函数、过程可用,但不写任何逻辑代码。比如 FUNCTION get_salary(emp_id NUMBER) RETURN NUMBER; 这一行只是告诉别人“我能算工资”,没说怎么算。
常见错误是把实现代码(比如 BEGIN ... END;)误写进 SPEC,Oracle 会直接报错 PLS-00103: Encountered the symbol "BEGIN" —— 因为 SPEC 不允许执行块。
- 必须用
CREATE OR REPLACE PACKAGE创建,不能用CREATE OR REPLACE PACKAGE BODY - 所有在 SPEC 中声明的子程序,名称、参数个数、类型、顺序、返回类型,必须和 BODY 中完全一致
- 删除时用
DROP PACKAGE pkg_name,会同时删掉 SPEC 和 BODY(如果存在)
包体(BODY)负责实现,可含私有内容
包体是“黑盒”,真正干活的地方。它实现 SPEC 中声明的所有函数和过程,还可以额外定义 SPEC 里没有的私有变量、私有函数、私有游标 —— 这些对外不可见,只能被本包内其他子程序调用。
比如你在 BODY 里写了个辅助函数 validate_input(p_val VARCHAR2),但没在 SPEC 里声明,那外部调用 pkg_name.validate_input 就会报 PLS-00302: component 'VALIDATE_INPUT' must be declared。
- 必须先成功创建 SPEC,才能创建 BODY;否则报
ORA-04043: object pkg_name does not exist - BODY 中的子程序定义不能带
CREATE OR REPLACE,直接写FUNCTION ... IS BEGIN ... END; - 初始化代码(
BEGIN ... END;块)可以放在 BODY 最后,用于首次加载时执行一次(如预热缓存)
调用时必须带包名前缀
无论是函数还是过程,只要封装在包里,调用时就必须显式带上包名,比如 emp_pkg.update_sal('smith', 5000)。漏掉 emp_pkg. 就会报 PLS-00201: identifier 'UPDATE_SAL' must be declared —— Oracle 默认只查当前作用域,不会自动搜索已安装的包。
跨 schema 调用还要加 schema 名:hr.emp_pkg.get_dept_name(10)。注意大小写敏感:如果包是用双引号建的(如 "MyPkg"),调用时也得严格匹配大小写和引号。
- SQL 语句中只能调用返回值的函数,不能调用过程;过程只能用
EXEC或BEGIN ... END;调用 - 函数若在 SELECT 中使用,必须是纯函数(无 DML、无包状态依赖),否则可能报
ORA-14551: cannot perform a DML operation inside a query
编译失败时错误定位容易混淆
当你改了 BODY 报错,别急着删重建。先查 USER_ERRORS:SELECT line, position, text FROM user_errors WHERE name = 'EMP_PKG' AND type = 'PACKAGE BODY';。很多问题其实出在 SPEC 已失效(比如依赖的表被删了),导致 BODY 编译不过,但错误提示却指向 BODY 某行——实际要先修复 SPEC 依赖或重新编译 SPEC:ALTER PACKAGE emp_pkg COMPILE PACKAGE;。
另一个典型坑:BODY 中引用了 SPEC 里声明的变量,但拼错了名字(比如 g_counter 写成 g_count),Oracle 报的是 PLS-00302: component 'G_COUNT' must be declared,但它不会告诉你这个变量是在 SPEC 还是 BODY 里声明的,得自己对照两边代码。











