包是一组相关过程、函数、变量、常量、类型、游标和异常等 PL/SQL 程序设计元素的组合。包具有面向对象设计的特点,是对这些 PL/SQL 程序设计元素的封装。包像一个容器或一个命名空间,可以将各种逻辑相关的过程、函数、变量、常量、类型、游标和异常结合在一起。为开发人员编写大型复杂应用程序提供良好的组织单元。当包被定义好后,应用程序可以通过包来访问各种不同的功能单元,而不用担心过多零散的子程序导致程序代码的松散。包具有简化应用设计、提高应用性能、实现信息隐藏、子程序重载等特点。对包中涉及的过程、函数、变量、常量、类型、游标和异常等 PL/SQL 程序设计元素如无特殊约束说明,其用法和现状保持一致。
南大通用在 GBase 8s 中兼容 ORACLE 的包及相关 PL/SQL 程序设计元素。
一个 PL/SQL 包由如下两部分组成:包头,或称为包的规范:主要是包的一些定义信息,不包含具体的代码实现部分,也可以说包头是 PL/SQL 程序和其他应用程序的接口,包含子程序、变量、常量、类型、游标和异常的声明。包体:是对包规范中声明的子程序和游标的实现部分,包体的内容对于外部应用程序来说是不可见的,包体就像是一个黑匣子一样,是对包规范的实现。注意:包头中子程序和游标是需要实现部分的,如果包头中没有声明子程序或游标,则包体就不是必须的。
DROP PACKAGE 会将包头和包体一并删除,也可以通过 DROP PACKAGE BODY 关键字只删除包体,此时包头依然有效。
包在首次引用时实例化并执行 initialize_section 部分,包为 SESSION 级,即多 SESSION 之间是不同的包实例。
下面我们通过几个例子示范包的常见用法,注意需要将 SQLMODE 设置为 ’ORACLE’ ,如果需要输出请设置 SERVEROUTPUT 为 ON 。
|
SET ENVIRONMENT SQLMODE 'ORACLE'; SET SERVEROUTPUT ON; |
|
CREATE PACKAGE pkg_demo1 AS companyName CONSTANT VARCHAR(100) := 'GBASE'; productName VARCHAR(100); END; |
本例子中,只有包头,并不需要包体。注意,常量不能使用变量赋初始值。然后我们创建一个过程,对包中声明的常量进行引用,包中的变量可以赋值和引用。
|
CREATE PROCEDURE p_demo1 AS BEGIN DBMS_OUTPUT.PUT_LINE(pkg_demo1.companyName); pkg_demo1.productName := 'GBase 8s'; DBMS_OUTPUT.PUT_LINE(pkg_demo1. productName); END; |
通过如下代码进行过程的调用。
|
CALL PROCEDURE p_demo1(); |
可以看到如下结果。
|
GBASE GBase 8s |
|
CREATE PACKAGE pkg_demo2 AS PROCEDURE helloWorld; FUNCTION myPlus(param1 IN INT, param2 IN INT) RETURN INT; END; |
然后我们创建包体,用于实现包头中声明的过程和函数。
|
CREATE PACKAGE BODY pkg_demo2 AS PROCEDURE helloWorld AS BEGIN DBMS_OUTPUT.PUT_LINE('HelloWorld!'); END; FUNCTION myPlus (param1 IN INT, param2 IN INT) RETURN INT AS BEGIN RETURN param1 + param2; EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('error'); END; END; |
我们创建一个过程,对包中声明的过程和函数进行引用。
|
CR EATE PROCEDURE p_demo2 AS BEGIN pkg_demo2.helloWorld; DBMS_OUTPUT.PUT_LINE(pkg_demo2.myPlus (2, 3)); END; |
通过如下代码进行过程的调用。
|
CALL PROCEDURE p_demo2(); |
可以看到如下结果。
|
HelloWorld! 5 |
|
CREATE PACKAGE pkg_demo3 AS PROCEDURE getCount; END;
CREATE PACKAGE BODY pkg_demo3 AS FCount INTEGER; PROCEDURE getCount AS BEGIN DBMS_OUTPUT.PUT_LINE('Current count is:'); DBMS_OUTPUT.PUT_LINE(FCount); FCount:= FCount + 1; END; BEGIN FCount := 1; END; |
在上面的例子中, pkg_demo3 在包头中声明了一个名称为 getCount 的过程,然后包体中声明了一个类型为 INTEGER 的私有变量,并对包头中声明的 getCount 过程进行了代码实现, getCount 过程实现输出私有变量 FCount 的值,并每次对 Fcount 进行 +1 操作。由于包是 SESSION 级的,因此我们可以看到在 call pkg_demo3.getCount() 多次引用时返回值会发生变化:第一次引用时输出的是 1 ,第二次引用时输出的是 2 ,第三次引用时输出的是 3 。特别注意的是此时如果另一个 SESSION 中引用 pkg_demo3 ,输出的不是 4 而是 1 ,即多 SESSION 之间是不同的包实例。
为了演示这个例子,我们需要创建一张包含 2 个字段的表。
|
CREATE TABLE TPERSON( FID VARCHAR(10) NOT NULL PRIMARY KEY, FName VARCHAR(50) ) |
并准备几条数据。
|
INSERT INTO TPERSON VALUES('1', 'first'); INSERT INTO TPERSON VALUES('2', 'second'); INSERT INTO TPERSON VALUES('3', 'third'); |
创建包头,包含一个整型的变量和一个返回类型是 TPERSON 表行记录类型的游标声明。
|
CREATE PACKAGE pkg_demo4 AS CURSOR myc RETURN TPERSON%ROWTYPE; END; |
创建包体,可以看到包体中对游标进行了代码实现。这里需要补充说明两点:一是包的实现部分也可以写在包头中,从而不创建包体,建议的方式是将游标的代码写在包体里,将定义和实现进行分离;二是对游标的 OPEN 、 FETCH 、 CLOSE 等操作一般建议写在调用包游标的地方。
|
CREATE PACKAGE BODY pkg _ demo4 AS CURSOR myc RETURN TPERSON%ROWTYPE IS SELECT FID, FName FROM TPERSON; END; |
然后我们创建一个过程,在过程中,通过 TYPE 定义了一个记录类型 TR ,并声明了一个类型为 TR 的 mt 变量。在过程的实现部分,首先通过 OPEN 打开了游标,然后遍历取数据并赋值给 mt ,并通过内置包 DBMS_OUTPUT 的 PUT 方法和 PUT_LINE 方法进行输出。
|
CREATE PROCEDURE p_demo4 AS TYPE TR IS RECORD( id VARCHAR(10), name VARCHAR(50) ); mt TR; BEGIN OPEN pkg_demo4.myc; FETCH pkg_demo4.myc INTO mt; WHILE pkg_demo4.myc%FOUND LOOP DBMS_OUTPUT.PUT(mt.id); DBMS_OUTPUT.PUT(': '); DBMS_OUTPUT.PUT_LINE(mt.name); FETCH pkg_demo4.myc INTO mt; END LOOP; CLOSE pkg_demo4.myc; END; |
最后我们通过如下代码进行过程的调用
|
CALL PROCEDURE p_demo4(); |
可以看到如下结果
|
1: first 2: second 3: third |
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69980453/viewspace-2773350/ ,如需转载,请注明出处,否则将追究法律责任。