包由包定义(PACKAGE)和包主体(PACKAGE BODY)两个部分组成:

  • 包定义(PACKAGE):包定义部分声明包内数据类型、变量、常量、游标、子程序和异常错误处理等元素,这些元素为包的公有元素。

  • 包主体(PACKAGE BODY):包主体则是包定义部分的具体实现,它定义了包定义部分所声明的游标和程序,在包主体中还可以声明包的私有元素。

包定义和包主体分开编译,并作为两部分分开的对象存放在数据库字典中,详见数据字典 user_source, all_source, dba_source。

创建包

创建包定义的语法如下:

  1. CREATE [OR REPLACE] PACKAGE package_name
  2. [AUTHID {CURRENT_USER | DEFINER}]
  3. {IS | AS}
  4. [公有数据类型定义[公有数据类型定义]…]
  5. [公有游标声明[公有游标声明]…]
  6. [公有变量、常量声明[公有变量、常量声明]…]
  7. [公有子程序声明[公有子程序声明]…]
  8. END [package_name];

其中,AUTHID CURRENT_USER 和 AUTHID DEFINER 选项说明应用程序在调用函数时所使用的权限模式。

创建包体定义的语法如下:

  1. CREATE [OR REPLACE] PACKAGE BODY package_name
  2. {IS | AS}
  3. [私有数据类型定义[私有数据类型定义]…]
  4. [私有变量、常量声明[私有变量、常量声明]…]
  5. [私有子程序声明和定义[私有子程序声明和定义]…]
  6. [公有游标定义[公有游标定义]…]
  7. [公有子程序定义[公有子程序定义]…]
  8. BEGIN
  9. PL/SQL 语句
  10. END [package_name];

示例

如下例所示,创建的包名称为 demo_pack, 该包中包含一个记录变量 DeptRec、两个函数和一个过程。

  1. CREATE OR REPLACE PACKAGE demo_pack
  2. IS
  3. DeptRec DEPARTMENTS%ROWTYPE;
  4. FUNCTION add_dept(dept_no NUMBER, dept_name VARCHAR2, location VARCHAR2)
  5. RETURN NUMBER;
  6. FUNCTION remove_dept(dept_no NUMBER)
  7. RETURN NUMBER;
  8. PROCEDURE query_dept(dept_no IN NUMBER);
  9. END demo_pack;

包主体的创建方法,它实现上面所声明的包定义,并在包主体中声明一个私有变量 flag 和一个私有函数 check_dept,由于在 add_dept 和 remove_dept 等函数中需要调用 check_dpet 函数,所以,在定义 check_dept 函数之前首先对该函数进行声明,这种声明方法称作前向声明。

  1. CREATE OR REPLACE PACKAGE BODY demo_pack
  2. IS
  3. FUNCTION add_dept
  4. (dept_no NUMBER, dept_name VARCHAR2, location VARCHAR2)
  5. RETURN NUMBER
  6. IS
  7. empno_remaining EXCEPTION;
  8. PRAGMA EXCEPTION_INIT(empno_remaining, -1);
  9. /* -1 是违反唯一约束条件的错误代码 */
  10. BEGIN
  11. INSERT INTO dept VALUES(dept_no, dept_name, location);
  12. IF SQL%FOUND THEN
  13. RETURN 1;
  14. END IF;
  15. EXCEPTION
  16. WHEN empno_remaining THEN
  17. RETURN 0;
  18. WHEN OTHERS THEN
  19. RETURN -1;
  20. END add_dept;
  21. FUNCTION remove_dept(dept_no NUMBER)
  22. RETURN NUMBER
  23. IS
  24. BEGIN
  25. DELETE FROM dept WHERE deptno=dept_no;
  26. IF SQL%FOUND THEN
  27. RETURN 1;
  28. ELSE
  29. RETURN 0;
  30. END IF;
  31. EXCEPTION
  32. WHEN OTHERS THEN
  33. RETURN -1;
  34. END remove_dept;
  35. PROCEDURE query_dept
  36. (dept_no IN NUMBER)
  37. IS
  38. BEGIN
  39. SELECT * INTO DeptRec FROM dept WHERE deptno=dept_no;
  40. EXCEPTION
  41. WHEN NO_DATA_FOUND THEN
  42. DBMS_OUTPUT.PUT_LINE('数据库中没有编码为'||dept_no||'的部门');
  43. WHEN TOO_MANY_ROWS THEN
  44. DBMS_OUTPUT.PUT_LINE('程序运行错误!');
  45. WHEN OTHERS THEN
  46. DBMS_OUTPUT.PUT_LINE(SQLCODE||'----'||SQLERRM);
  47. END query_dept;
  48. BEGIN
  49. Null;
  50. END demo_pack;

调用包的函数和方法

调用包里面的变量、常量、函数、方法等需要在对象名里包含包的名字,并用“.”分隔。即对包内共有元素的调用格式为:包名.元素名称

调用 demo_pack 包内函数对 dept 表进行插入、查询和修改操作,并通过 demo_pack 包中的记录变量 DeptRec 显示所查询到的数据库信息,如下例所示:

  1. obclient> DECLARE
  2. -> Var NUMBER;
  3. -> BEGIN
  4. -> Var := demo_pack.add_dept(900,'Administration', 'Beijing');
  5. -> IF var =-1 THEN
  6. -> DBMS_OUTPUT.PUT_LINE(SQLCODE||'----'||SQLERRM);
  7. -> ELSIF var =0 THEN
  8. -> DBMS_OUTPUT.PUT_LINE('该部门记录已经存在!');
  9. -> ELSE
  10. -> DBMS_OUTPUT.PUT_LINE('添加记录成功!');
  11. -> Demo_pack.query_dept(900);
  12. -> DBMS_OUTPUT.PUT_LINE(demo_pack.DeptRec.deptno||'---'||demo_pack.DeptRec.dname||'---'||demo_pack.DeptRec.loc);
  13. -> var := demo_pack.remove_dept(900);
  14. -> IF var =-1 THEN
  15. -> DBMS_OUTPUT.PUT_LINE(SQLCODE||'----'||SQLERRM);
  16. -> ELSIF var=0 THEN
  17. -> DBMS_OUTPUT.PUT_LINE('该部门记录不存在!');
  18. -> ELSE
  19. -> DBMS_OUTPUT.PUT_LINE('删除记录成功!');
  20. -> END IF;
  21. -> END IF;
  22. -> END;
  23. -> /
  24. Query OK, 0 rows affected (0.65 sec)
  25. 添加记录成功!
  26. 900---Administration---Beijing
  27. 删除记录成功!