Allow the Oralcel server to read multiple objects into memory at once.
Package bundle related PL/SQL types, items, and subprograms into one container.
包含2部分 : specification , body ( stored separately in the database )
The specification is the interface to your applications. ( 声明 )
body : 真实定义
The package itself cannot be called . package once written and compiled, the contents can be shared by many applications.
When you call a packaged PL/SQL construct for the first time, the whole package is loaded into memory. ( thus, later calls to constructs in the same package require no disk input/output I/O )
Public package : are those that are declared in the package specification and defined in the package body.
Private package : are those that are defined solely within the package body.
变量可见度
G_VARIABLE_NAME ( g 前缀开头的是 Global )
A package specification can exist without a package body, but a package body cannot exist without a package specification.
声明: ( specification )
CREATE [ OR REPLACE ] PACKAGE package_name
IS | AS
public type and item declarations
subprogram specifications
END package_name;
例子 :
包体 ( body )
CREATE [ OR REPLACE ] PACKAGE BODY package_name
IS | AS
private type and item declarations
subprogram bodies
END package_name ;
It is quite common in the package body to see all private variables and subprograms defined first and the public subprograms defined last ( 先定义似有的, 再定义公共变量 )
例子 :
调用 :
EXECUTE comm_package.reset_comm( 0.15 )
EXECUTE scott.comm_package.reset_comm( 0.15 ) // different schema
EXECUTE comm_package.reset_comm@ny( 0.15 ) // remote database
可以只有 specification , 没有 body.
删除 :
DROP PACKAGE package_name ;
DROP PACKAGE BODY package_name;
包里的东西可以重载,比如 you can careate multiple subprograms with the same name in the same package, each taking parameters of different number or datatype.
重载例子 :
例如: 系统中的 TO_CHAR, 之类的,就是重载,可以接受不同参数。
You must declare an identifier before using it. Therefore, a subprogram must be declared before calling it.
( 必须先声明,或者在 specification 中声明,或者在 body中声明,总之,真实定义之前必须要声明 )
比如当系统在一个 procedure 中调用另一个 procedure , 但是,该 procedure 没有被声明或定义,那么它就不知道如何调用 ( 定义或声明要在这个 procedure 之前 )
calc_rating(), 被调用了,但是 它的定义在下边,所以就会出错。
最好还是声明,因为如果两个 procedure 互相调用时,哪个放在前边也是不行的。
包中,函数 Functions 的限制 : ( 跟之前单体函数的限制差不多 )
- A function called from a query or DML statement may not end the current transaction , create or roll back to a savepoint, or alter the system or session
- A function called from a query statement or from a parallelized DML statement may not execute a DML statement or otherwise modify the database
- A function called from a DML statement may not read or modify the particular table being modified by that DML statement.
调用自己定义包中的函数
SELECT taxes_pack.tax( salary ) , salary, last_name FROM employees; // taxes_pack 包名
Oracle Supply package. ( 貌似只有少部分有用 )
Most of the standard packages are created by running catproc.sql
- 动态 ( 带参数的 SQL ) ( 顺序 : parse –> bind –> execute –> fetch )
DBMS_SQL package 提供了 dynamic SQL .
例如 :
- DBMS_DDL package
- DBMS_JOB Subprograms
- DBMS_OUTPUT
PUT, NEW_LINE, PUT_LINE, GET_LINE, GET_LINES, ENABLE/DISABLE
1. 为什么使用包
答:在一个大型项目中,可能有很多模块,而每个模块又有自己的过程、函数等。而这些过程、函数默认是放在一起的(如在PL/SQL中,过程默认都是放在一起的,即Procedures中),这些非常不方便查询和维护,甚至会发生误删除的事件。所以通过使用包就可以分类管理过程和函数。
有点类似 object – c
包分两部分,包规范和包体
2. 定义包说明
--定义包规范
create or replace package p_stu
as
--定义结构体
type re_stu is record(
rname student.name%type,
rage student.age%type
);
--定义游标
type c_stu is ref cursor;
--定义函数
function numAdd(num1 number,num2 number)return number;
--定义过程
procedure GetStuList(cid in varchar2,c_st out c_stu);
end;
3. 定义包体
--实现包体,名称一致。
create or replace package body p_stu
as
--游标和结构体,包规范中已声明,包体中不用再声明,直接使用。
--实现方法
function numAdd(num1 number,num2 number)return number
as
num number;
begin
num:=num1+num2;
return num;
end;
--实现过程
procedure GetStuList(cid varchar2,c_st out c_stu)
as
r_stu re_stu; --直接使用包规范中的结构
begin
open c_st for select name,age from student where classid=cid;
-- 如果已经在过程中遍历了游标,在使用这个过程的块中,将没有值。
-- loop
-- fetch c_st into r_stu;
-- exit when c_st%notfound;
-- dbms_output.put_line('姓名='||r_stu.rname);
-- end loop;
end;
end;
4. 使用包
declare
c_stu p_stu.c_stu; --定义包中游标变量
r_stu p_stu.re_stu; --定义包中结构体变量
num number;
begin
--使用及遍历包中过程返回的结果集
p_stu.GetStuList('C001',c_stu);
loop
fetch c_stu into r_stu;
exit when c_stu%notfound;
dbms_output.put_line('姓名='||r_stu.rname);
end loop;
--使用包中的方法
select p_stu.numAdd(5,6) into num from dual;
dbms_output.put_line('Num='||num);
end;
一、程序包的相关知识
1.定义与说明
a. 相关对象的封装
b. 程序包的各部分
- 程序包规格说明
声明子程序
- 程序包主体
定义子程序
2.使用程序包的优点
- 模块化
- 更轻松的应用程序设计
- 信息隐藏
- 新增功能
- 性能更佳
3.公有项和私有项的区别
公有项:在程序包说明部分定义的变量、过程、函数
私有项:在程序包主体部分定义的变量、过程、函数
公有项 私有项
可以在程序包之外引用 不能在程序包之外引用
是在程序包规格说明中定义的 是在程序包主体中定义的
用于全局目的 用于局部目的
二、程序包创建说明
1.程序包规格说明
(1)、使用Create Package命令进行创建
(2)、包含公用对象和类型
(3)、声明类型、常量、变量、异常、游标和子程序
(4)、可以在没有程序包主题的情况下存在
(5)、可以重载
- 程序包中的多个子程序可以具有相同的名称
- 它们的形参是不同的
- 只能位于打包的子程序中
- 限制
a. 如果子程序的参数仅名称或模式不同,则不能重载
b. 不能基于其返回类型重载子程序
2.程序包主体
(1)、使用Create Package body 命令进行创建
(2)、包含子程序和游标的定义
(3)、包含私有声明
(4)、不能在没有程序包规格说明的情况下独立存在
3.程序包的调用
包名.类型名;
包名.函数名[参数表];
包名..过程名[参数表];
(1)、 Package-name.type-name
(2)、 Package-name.object-name
(3)、 Package-name.subprogram-name
其中,Package-name 是程序包名称,type-name是类型名称,
object-name是对象名称,subprogram-name 是子程序名称
--示例
DBMS_output.put_line(Hello);
(4)、对于返回参数是游标类型的调用(如:引用游标)
set autoprint on --打开Sqlplus输出
variable tempCur RefCursor; --定义一个宿主类型的引用游标变量
exec StudentPackage.ReturnStudent(:tempCur); --执行带有引用游标的过程 注意使用宿主类型的变量前面要加“:”符号
4. 有关子程序和程序包的信息
A.数据字典
User_objects 用于检查对象是否存在
User_source 用于获取对象的代码
B. 包的修改和删除
Alter Package [Body] 包名
Alter Package Body StudentPackage;
Drop Package [Body] 包名
Drop Package Body StudentPackage;
5. 创建格式
A.创建包规格部分
格式:Create [or replace] Package 包名
IS|AS
变量声明;
类型定义;
异常声明;
游标声明;
函数说明;
过程说明;
Pragma restrict_references(过程名或函数名,WNDS[,WNPS][,RNDS][,RNPS]) --编译指令 限定函数的操作
End [包名];
B.创建包主体部分
格式: Create [or replace] package body 包主体名 --包主体名一定要是已经定义的包名
IS|AS
变量声明; --具体的实现部分
类型定义;
异常声明;
游标声明;
函数说明;
过程定义;
End [包主体名];
6. 示例
示例1.创建程序包的规格说明部分
1 Create or replace Package StudentPackage 2 is 3 Type curRefStudent is Ref Cursor Return Student%rowtype; 4 Procedure SelectStudent(FindID in Student.stuid%type); 5 Procedure InsertStudent(NewStudent in Student%rowType); 6 Procedure UpdateStudent(NewStudent in Student%rowType); 7 Procedure DeleteStudent(DelID in Student.stuid%type); 8 Procedure ReturnStudent(inOutstu in out curRefStudent); 9 Function RegurnRecordCount Return Number; 10 End studentPackage;
示例2.创建程序包的主体部分
1 Create or replace Package Body StudentPackage IS 2 Procedure SelectStudent (FindID in Student.stuid%type) as --注意没有Create 3 /*实现查询过程的定义*/ 4 Cursor findCur is select * from student where stuid=FindID; 5 Begin 6 For S in FindCur Loop 7 DBMS_output.put_line(S.stuID||' '||s.StuName||' '||S.Sex); 8 End Loop; 9 Exception 10 When No_Data_Found Then 11 DBMS_output.Put_Line('没有查到ID为'||FindID||'的记录!'); 12 When Others Then 13 DBMS_output.Put_Line('查询过程中发生意外情况'); 14 End SelectStudent; --结束查询过程 15 /*实现插入过程的定义*/ 16 Procedure InsertStudent(NewStudent in Student%rowType) as 17 iRec Integer; 18 Not_Exists_Student Exception; --预定义异常 19 Begin 20 Select count(*) into iRec from student where stuid=NewStudent.stuID; 21 IF iRec>0 Then 22 Raise Not_Exists_Student; 23 Else 24 insert into student values(NewStudent.stuid,NewStudent.stuName,NewStudent.sex); 25 commit; 26 End IF; 27 Exception 28 When Not_Exists_Student Then 29 DBMS_output.Put_Line('要插入的编号:'||NewStudent.stuid||'的记录已经存在'); 30 When Others Then 31 DBMS_output.Put_Line('插入记录操作过程中出现错误'); 32 End InsertStudent;--结束插入过程 33 /*实现更新过程的定义*/ 34 Procedure UpdateStudent(NewStudent in Student%rowType) as 35 iRec Integer; 36 Begin 37 select Count(*) into iRec From student Where stuid=NewStudent.stuid; 38 IF iRec =0 Then 39 DBMS_output.Put_Line('编号为:'||NewStudent.stuid||'的记录不存在,修改失败'); 40 ELSE 41 Update student Set Stuname=NewStudent.stuName,Sex=NewStudent.Sex 42 WHERE stuid=NewStudent.stuID; 43 Commit; 44 End IF; 45 Exception 46 When No_Data_Found Then 47 DBMS_output.Put_Line('编号为:'||NewStudent.stuID||'的记录不存在,修改失败'); 48 When Others Then 49 DBMS_output.Put_Line('执行修改操作时发生意外情况,修改未成功'); 50 End UpdateStudent;--结束修改过程 51 /*实现删除过程的定义*/ 52 Procedure DeleteStudent(DelID in Student.stuid%type) as 53 iRec Integer; 54 Begin 55 Select Count(*) into iRec From Student Where stuID=DelID; 56 IF iRec=0 Then 57 DBMS_output.Put_Line('编号为:'||DelID||'的记录不存在,删除操作时未成功'); 58 ELSE 59 Delete From student Where stuid=DelID; 60 Commit; 61 DBMS_output.Put_Line('删除成功!'); 62 End IF; 63 Exception 64 When Others Then 65 DBMS_output.Put_Line('执行删除操作时发生意外情况,删除未成功'); 66 End DeleteStudent; 67 /*实现参数带有游标类型定义*/ 68 Procedure ReturnStudent(inOutstu in out curRefStudent) as 69 Begin 70 Open inOutstu For Select * from student; 71 End ReturnStudent; 72 /*实现函数定义*/ 73 Function RegurnRecordCount Return Number as 74 RecCount number(10); 75 Begin 76 Select Count(*) into RecCount From student; 77 Return recCount; 78 Exception 79 When Others Then 80 DBMS_output.Put_Line('查询表中记录数时发生意外情况'); 81 End RegurnRecordCount; --结束函数的定义 82 End studentPackage;--结束包
示例3:调用包
1. 调用studentPackage包中的InsertStudent过程
1 Declare 2 newStu Student%RowType; 3 Begin 4 newStu.stuID:='1001'; 5 newStu.stuName:='张三'; 6 newStu.sex:='男'; 7 studentPackage.InsertStudent(newStu); 8 End; 9 /
2. 调用studentPackage包中的UpdateStudent过程
1 Declare 2 newStu Student%RowType; 3 Begin 4 newStu.stuID:='1001'; 5 newStu.stuName:='李四'; 6 newStu.sex:='女'; 7 studentPackage.UpdateStudent(newStu); 8 Exception 9 When Dup_Val_On_Index Then 10 DBMS_output.Put_Line('唯一约束被破坏'); 11 When Others Then 12 DBMS_output.Put_Line('更新过程出现错误'); 13 End; 14 /
3. 调用studentPackage包中的DeleteStudent过程
1 Begin 2 studentPackage.DeleteStudent('1001'); 3 End; 4 /
4. 调用studentPackage包中的ReturnRecordCount函数
1 Begin 2 DBMS_output.put_Line(studentPackage.ReturnRecordCount); 3 End; 4 /