1个回答
展开全部
一、什么是PL/SQL包
包就是一个把各种逻辑相关的类型、常量、变量、异常和子程序组合在一起的模式对象。包通常由两个部分组成:包说明和包体,但有时包体是不需要的。说明(简写为spec)是应用程序接口;它声明了可用的类型、变量、常量、异常、游标和子程序,包体部分完全定义游标和子程序,并对说明中的内容加以实现。
如下例所示,我们可以认为说明部分是一个可选接口,而包体是一个"黑盒"。我们可以调试、增强或替换一个包体而不同改变接口(包说明)。
我们可以从SQL*Plus中使用CREATE PACKAGE语句来创建一个包。语法如下:
CREATE [OR REPLACE] PACKAGE package_name
[AUTHID {CURRENT_USER | DEFINER}]
{IS | AS}
[PRAGMA SERIALLY_REUSABLE;]
[collection_type_definition ...]
[record_type_definition ...]
[subtype_definition ...]
[collection_declaration ...]
[constant_declaration ...]
[exception_declaration ...]
[object_declaration ...]
[record_declaration ...]
[variable_declaration ...]
[cursor_spec ...]
[function_spec ...]
[procedure_spec ...]
[call_spec ...]
[PRAGMA RESTRICT_REFERENCES(assertions) ...]
END [package_name];
[CREATE [OR REPLACE] PACKAGE BODY package_name {IS | AS}
[PRAGMA SERIALLY_REUSABLE;]
[collection_type_definition ...]
[record_type_definition ...]
[subtype_definition ...]
[collection_declaration ...]
[constant_declaration ...]
[exception_declaration ...]
[object_declaration ...]
[record_declaration ...]
[variable_declaration ...]
[cursor_body ...]
[function_spec ...]
[procedure_spec ...]
[call_spec ...]
[BEGIN
sequence_of_statements]
END [package_name];]
在说明部分声明的内容都是公有的,对应用程序是可见的。我们必须在所有的其他内容(除了用于为一个特殊的函数命名的编译指示;这样的编译指示必须跟在函数说明之后)声明之后才可以声明子程序。
包体中的内容有私有的,它实现了说明部分定义的细节内容,并且对应用程序是不可见的。紧跟着包体声明部分的是一个可选的初始化部分,它用于初始化包中的变量等。
AUTHID语句决定了是否是所有的打包子程序都按定义者权限(默认)或调用者权限执行,其中涉及到的模式对象是在定义者的模式中解析还是在调用者的模式中解析。
一个调用说明能让我们在Oracle数据词典中发布一个Java方法或外部C函数。调用说明靠把程序的名称、参数类型和返回类型映射到它们的SQL副本(SQL counterpart)中来发布程序。
1、PL/SQL包举例
在下面的例子中,我们把一个记录类型、游标和两个employment过程进行打包。要注意,过程hire_employee使用数据库序列empno_seq和函数SYSDATE分别插入到字段雇员编号和雇佣日期。
CREATE OR REPLACE PACKAGE emp_actions AS -- spec
TYPE emprectyp IS RECORD(
emp_id INT,
salary REAL
);
CURSOR desc_salary RETURN emprectyp;
PROCEDURE hire_employee(
ename VARCHAR2,
job VARCHAR2,
mgr NUMBER,
sal NUMBER,
comm NUMBER,
deptno NUMBER
);
PROCEDURE fire_employee(emp_id NUMBER);
END emp_actions;
CREATE OR REPLACE PACKAGE BODY emp_actions AS -- body
CURSOR desc_salary RETURN emprectyp IS
SELECT empno, sal
FROM emp
ORDER BY sal DESC;
PROCEDURE hire_employee(
ename VARCHAR2,
job VARCHAR2,
mgr NUMBER,
sal NUMBER,
comm NUMBER,
deptno NUMBER
) IS
BEGIN
INSERT INTO emp
VALUES (empno_seq.NEXTVAL,
ename,
job,
mgr,
SYSDATE,
sal,
comm,
deptno);
END hire_employee;
PROCEDURE fire_employee(emp_id NUMBER) IS
BEGIN
DELETE FROM emp
WHERE empno = emp_id;
END fire_employee;
END emp_actions;
只有在包说明部分的声明内容对应用程序才是可见可访问的;包体的详细实现是不可见不可访问的。所以,我们可以在不重新编译调用程序的前提下修改包体(实现)。
二、PL/SQL包的优点
包提供了几个优点:模块化、方便应用程序设计、信息隐藏、附加功能和良好的性能。
模块化
包能让我们把逻辑相关的类型、常量、变量、异常和子程序等放到一个命名的PL/SQL模块中。每一个包都容易理解,包与包之间接口简单、清晰。这将有助于程序开发。
轻松的程序设计
设计应用程序时,我们首先要确定的是包说明中的接口信息。我们可以在没有包体的条件下编写并编译说明部分。然后引用该包的存储子程序也会被编译。在完成整个应用程序之前,我们是不需要完全实现包体部分的。
信息隐藏
有了包,我们就可以指定哪些类型、常量、变量、异常和子程序等是公有(可见和可访问)或私有(隐藏和不可访问)。例如,如果一个包里包含了四个子程序,其中三个是公有的一个是私有的。包就会隐藏私有子程序的实现,这样的话,如果实现内容发生改变,受到影响的只有包本身(不是我们的应用程序)。同样,对用户隐藏实现细节也能保证包的完整性。
附加功能
打包公有变量和游标在一个会话期会一直存在。所以,它们可以被当前环境下的所有子程序共享。并且它们允许我们跨事务来维护数据而不用把它保存在数据库中。
良好的性能
在我们首次调用打包子程序时,整个包就会被加载到内存中。所以,以后调用包中的相关子程序时,就不需要再次读取磁盘了。包能阻塞级联依赖,这样就能避免不必要的编译。例如,如果我们改变打包函数的实现,Oracle不需要重新编译调用子程序,因为它们并不依赖于包体。
三、理解包说明
包说明包括了公有声明。这些声明的作用于对于数据库模式来说是本地的,对于包来说是全局的。所以,被声明的内容可以从应用程序中和包的任何地方访问。下图演示了包的作用范围:
说明中列出了包中对应用程序所有可用的资源。例如,下面的声明演示了一个接受INTEGER类型的参数并返回一个INTEGER结果的函数fac:
FUNCTION fac (n INTEGER) RETURN INTEGER; -- returns n!
这些就是我们要调用的函数的所有信息。我们并不需要考虑它的实现细节(如,是使用迭代还是递归)。
只有子程序和游标有实现部分。所以,如果一个说明只有类型、常量、变量、异常的声明和调用说明,那么包体就没有必要的了。下面就是一个没有包体的包:
CREATE PACKAGE trans_data AS -- bodiless package
TYPE timerec IS RECORD(
minutes SMALLINT,
hours SMALLINT
);
TYPE transrec IS RECORD(
CATEGORY VARCHAR2,
ACCOUNT INT,
amount REAL,
time_of timerec
);
minimum_balance CONSTANT REAL := 10.00;
number_processed INT;
insufficient_funds EXCEPTION;
END trans_data;
包trans_data不需要包体,因为类型、常量、变量和异常并没有实现部分。这样的包能让我们定义全局变量,可供子程序和数据库触发器使用。
1、引用包的内容
如果要引用包内声明的类型、常量、变量、异常和子程序等,就得使用点标识:
package_name.type_name
package_name.item_name
package_name.subprogram_name
package_name.call_spec_name
我们可以从数据库触发器、存储子程序、3GL(第三代程序语言)应用程序和各种Oracle工具中引用包中的内容。例如,我们可以从SQL*Plus中调用过程hire_employee:
SQL> CALL emp_actions.hire_employee('TATE', 'CLERK', ...);
下例中,我们可以从一个嵌入到Pro*C的匿名PL/SQL块调用同样的过程。实参emp_name和job_title是主变量(即声明在主环境中的变量)。
EXEC SQL EXECUTE
BEGIN
emp_actions.hire_employee(:emp_name, :job_title, ...);
约束
我们不能直接或间接地引用远程打包变量。例如,我们不能远程调用下面的过程,因为它在参数初始化子句中引用了打包变量:
CREATE PACKAGE random AS
seed NUMBER;
PROCEDURE initialize (starter IN NUMBER := seed, ...);
同样,我们也不能在包的内部引用主变量。
^ ^希望对你有帮助
包就是一个把各种逻辑相关的类型、常量、变量、异常和子程序组合在一起的模式对象。包通常由两个部分组成:包说明和包体,但有时包体是不需要的。说明(简写为spec)是应用程序接口;它声明了可用的类型、变量、常量、异常、游标和子程序,包体部分完全定义游标和子程序,并对说明中的内容加以实现。
如下例所示,我们可以认为说明部分是一个可选接口,而包体是一个"黑盒"。我们可以调试、增强或替换一个包体而不同改变接口(包说明)。
我们可以从SQL*Plus中使用CREATE PACKAGE语句来创建一个包。语法如下:
CREATE [OR REPLACE] PACKAGE package_name
[AUTHID {CURRENT_USER | DEFINER}]
{IS | AS}
[PRAGMA SERIALLY_REUSABLE;]
[collection_type_definition ...]
[record_type_definition ...]
[subtype_definition ...]
[collection_declaration ...]
[constant_declaration ...]
[exception_declaration ...]
[object_declaration ...]
[record_declaration ...]
[variable_declaration ...]
[cursor_spec ...]
[function_spec ...]
[procedure_spec ...]
[call_spec ...]
[PRAGMA RESTRICT_REFERENCES(assertions) ...]
END [package_name];
[CREATE [OR REPLACE] PACKAGE BODY package_name {IS | AS}
[PRAGMA SERIALLY_REUSABLE;]
[collection_type_definition ...]
[record_type_definition ...]
[subtype_definition ...]
[collection_declaration ...]
[constant_declaration ...]
[exception_declaration ...]
[object_declaration ...]
[record_declaration ...]
[variable_declaration ...]
[cursor_body ...]
[function_spec ...]
[procedure_spec ...]
[call_spec ...]
[BEGIN
sequence_of_statements]
END [package_name];]
在说明部分声明的内容都是公有的,对应用程序是可见的。我们必须在所有的其他内容(除了用于为一个特殊的函数命名的编译指示;这样的编译指示必须跟在函数说明之后)声明之后才可以声明子程序。
包体中的内容有私有的,它实现了说明部分定义的细节内容,并且对应用程序是不可见的。紧跟着包体声明部分的是一个可选的初始化部分,它用于初始化包中的变量等。
AUTHID语句决定了是否是所有的打包子程序都按定义者权限(默认)或调用者权限执行,其中涉及到的模式对象是在定义者的模式中解析还是在调用者的模式中解析。
一个调用说明能让我们在Oracle数据词典中发布一个Java方法或外部C函数。调用说明靠把程序的名称、参数类型和返回类型映射到它们的SQL副本(SQL counterpart)中来发布程序。
1、PL/SQL包举例
在下面的例子中,我们把一个记录类型、游标和两个employment过程进行打包。要注意,过程hire_employee使用数据库序列empno_seq和函数SYSDATE分别插入到字段雇员编号和雇佣日期。
CREATE OR REPLACE PACKAGE emp_actions AS -- spec
TYPE emprectyp IS RECORD(
emp_id INT,
salary REAL
);
CURSOR desc_salary RETURN emprectyp;
PROCEDURE hire_employee(
ename VARCHAR2,
job VARCHAR2,
mgr NUMBER,
sal NUMBER,
comm NUMBER,
deptno NUMBER
);
PROCEDURE fire_employee(emp_id NUMBER);
END emp_actions;
CREATE OR REPLACE PACKAGE BODY emp_actions AS -- body
CURSOR desc_salary RETURN emprectyp IS
SELECT empno, sal
FROM emp
ORDER BY sal DESC;
PROCEDURE hire_employee(
ename VARCHAR2,
job VARCHAR2,
mgr NUMBER,
sal NUMBER,
comm NUMBER,
deptno NUMBER
) IS
BEGIN
INSERT INTO emp
VALUES (empno_seq.NEXTVAL,
ename,
job,
mgr,
SYSDATE,
sal,
comm,
deptno);
END hire_employee;
PROCEDURE fire_employee(emp_id NUMBER) IS
BEGIN
DELETE FROM emp
WHERE empno = emp_id;
END fire_employee;
END emp_actions;
只有在包说明部分的声明内容对应用程序才是可见可访问的;包体的详细实现是不可见不可访问的。所以,我们可以在不重新编译调用程序的前提下修改包体(实现)。
二、PL/SQL包的优点
包提供了几个优点:模块化、方便应用程序设计、信息隐藏、附加功能和良好的性能。
模块化
包能让我们把逻辑相关的类型、常量、变量、异常和子程序等放到一个命名的PL/SQL模块中。每一个包都容易理解,包与包之间接口简单、清晰。这将有助于程序开发。
轻松的程序设计
设计应用程序时,我们首先要确定的是包说明中的接口信息。我们可以在没有包体的条件下编写并编译说明部分。然后引用该包的存储子程序也会被编译。在完成整个应用程序之前,我们是不需要完全实现包体部分的。
信息隐藏
有了包,我们就可以指定哪些类型、常量、变量、异常和子程序等是公有(可见和可访问)或私有(隐藏和不可访问)。例如,如果一个包里包含了四个子程序,其中三个是公有的一个是私有的。包就会隐藏私有子程序的实现,这样的话,如果实现内容发生改变,受到影响的只有包本身(不是我们的应用程序)。同样,对用户隐藏实现细节也能保证包的完整性。
附加功能
打包公有变量和游标在一个会话期会一直存在。所以,它们可以被当前环境下的所有子程序共享。并且它们允许我们跨事务来维护数据而不用把它保存在数据库中。
良好的性能
在我们首次调用打包子程序时,整个包就会被加载到内存中。所以,以后调用包中的相关子程序时,就不需要再次读取磁盘了。包能阻塞级联依赖,这样就能避免不必要的编译。例如,如果我们改变打包函数的实现,Oracle不需要重新编译调用子程序,因为它们并不依赖于包体。
三、理解包说明
包说明包括了公有声明。这些声明的作用于对于数据库模式来说是本地的,对于包来说是全局的。所以,被声明的内容可以从应用程序中和包的任何地方访问。下图演示了包的作用范围:
说明中列出了包中对应用程序所有可用的资源。例如,下面的声明演示了一个接受INTEGER类型的参数并返回一个INTEGER结果的函数fac:
FUNCTION fac (n INTEGER) RETURN INTEGER; -- returns n!
这些就是我们要调用的函数的所有信息。我们并不需要考虑它的实现细节(如,是使用迭代还是递归)。
只有子程序和游标有实现部分。所以,如果一个说明只有类型、常量、变量、异常的声明和调用说明,那么包体就没有必要的了。下面就是一个没有包体的包:
CREATE PACKAGE trans_data AS -- bodiless package
TYPE timerec IS RECORD(
minutes SMALLINT,
hours SMALLINT
);
TYPE transrec IS RECORD(
CATEGORY VARCHAR2,
ACCOUNT INT,
amount REAL,
time_of timerec
);
minimum_balance CONSTANT REAL := 10.00;
number_processed INT;
insufficient_funds EXCEPTION;
END trans_data;
包trans_data不需要包体,因为类型、常量、变量和异常并没有实现部分。这样的包能让我们定义全局变量,可供子程序和数据库触发器使用。
1、引用包的内容
如果要引用包内声明的类型、常量、变量、异常和子程序等,就得使用点标识:
package_name.type_name
package_name.item_name
package_name.subprogram_name
package_name.call_spec_name
我们可以从数据库触发器、存储子程序、3GL(第三代程序语言)应用程序和各种Oracle工具中引用包中的内容。例如,我们可以从SQL*Plus中调用过程hire_employee:
SQL> CALL emp_actions.hire_employee('TATE', 'CLERK', ...);
下例中,我们可以从一个嵌入到Pro*C的匿名PL/SQL块调用同样的过程。实参emp_name和job_title是主变量(即声明在主环境中的变量)。
EXEC SQL EXECUTE
BEGIN
emp_actions.hire_employee(:emp_name, :job_title, ...);
约束
我们不能直接或间接地引用远程打包变量。例如,我们不能远程调用下面的过程,因为它在参数初始化子句中引用了打包变量:
CREATE PACKAGE random AS
seed NUMBER;
PROCEDURE initialize (starter IN NUMBER := seed, ...);
同样,我们也不能在包的内部引用主变量。
^ ^希望对你有帮助
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询
广告 您可能关注的内容 |