写点什么

金仓数据库 KingbaseES PAKCAGE 的使用

作者:金仓技术
  • 2025-04-23
    四川
  • 本文字数:5217 字

    阅读完需:约 17 分钟

金仓数据库KingbaseES PAKCAGE的使用


什么是包


包是一种数据库对象,相当于一个容器。将逻辑上相关的过程、函数、变量等元素组合成一个更大的单元。

包的组成

包由包头和包体两部分组成。包头中声明的元素是公有对象,对外部可见;包体中声明的元素为该包的私有对象,仅该包可见;包体中自定义所有公有对象和私有对象。

包头中可以声明公有变量、常量、类型、自定义异常、游标和子程序。

包体中可以声明私有变量、常量、类型、自定义异常、游标和子程序。也可以定义上述所有对象,另外,包体中可以定义自己的初始化块,初始化块仅在创建时执行,可以在创建时将包中定义的变量等初始化。

包的优点

1.简化应用设计

包将一个模块的 PLSQL 程序元素组织到一起,每个包相互独立,便于管理。

2.提高应用性能

包在首次调用时,就整体存入内存,之后的访问都不需要再进行 I/O 操作。

3.实现信息隐藏

包中分为公有和私有部分,只对外提供公有的接口,私有的内容对外不可见。

4.解决命名冲突

在不同的包中,过程、函数、变量等元素都可以重名,解决了命名冲突问题。

包的使用规则

  1. 包头可以单独存在,但是包体的定义一定要在包头定义之后。

  2. 包体中必须定义所有包头中声明的对象。

  3. 允许创建不包含任何函数或存储过程的包头和包体。

  4. 包体中的私有元素不能被外部直接使用,但可以通过公有子程序间接使用。

  5. 如果更新包头,则必须重新编译包体后才能正常使用;如果仅更改包体,则无需重新编译包头。

包的使用规则场景

1. 单独定义一个包头,正确执行;单独定义一个包体,报错。

--定义包头,创建成功
test=# CREATE OR REPLACE PACKAGE pkg1 AS
test-#     i int;
test-#     PROCEDURE pt1();
test-# END;
test-# /
CREATE PACKAGE 


--定义包体,报错
test=# CREATE OR REPLACE PACKAGE BODY pkg2 AS
test-#     PROCEDURE pt1() AS
test-#     BEGIN
test-#         NULL;
test-#     END;
test-# END;
test-# /
ERROR:  package "pkg2" does not exist
复制代码


2. 包体中未定义包头中所有声明的对象

--创建一个包头,声明两个存储过程pt1,pt2
test=# CREATE OR REPLACE PACKAGE pkg3 AS
test-#     PROCEDURE pt1();
test-#     PROCEDURE pt2();
test-# END;
test-# /
CREATE PACKAGE


--创建对应的包体,只定义其中一个存储过程pt1。报错提示pt2也必须在包体中定义
test=# CREATE OR REPLACE PACKAGE BODY pkg3 AS
test-#     PROCEDURE pt1() AS       --只定义了pt1,未定义pt2
test-#     BEGIN
test-#         RAISE NOTICE 'pt1';
test-#     END;
test-# END;
test-# /
ERROR:  procedure "pt2" is declared in package "pkg3" and must be defined in the package body
复制代码


3. 创建一个包头和一个包体,其中不声明定义任何元素

--先创建包头,不声明任何元素,创建成功
test=# CREATE OR REPLACE PACKAGE pkg4 AS
test-# END;
test-# /
CREATE PACKAGE
--再创建包体,不声明定义任何元素,创建成功
test=# CREATE OR REPLACE PACKAGE BODY pkg4 AS
test-# END;
test-# /
CREATE PACKAGE BODY
复制代码


4. 包体中定义私有元素无法再外部直接调用,但是可以通过公有子程序间接调用

--创建一个包头,其中声明了一个公有存储过程
test=# CREATE OR REPLACE PACKAGE pkg5 AS
test-#     PROCEDURE pubilc_procedure();
test-# END;
test-# /
CREATE PACKAGE


--创建一个包体,其中声明了一个私有变量,定义了一个包头中声明的公有过程和一个包头中未声明的私有过程(注意:私有子程序必须定义在公有子程序之前才能被公有子程序调用)
test=# CREATE OR REPLACE PACKAGE BODY pkg5 AS
test-#     i int;                                   --定义一个私有变量
test-#     PROCEDURE private_procedure() AS        --定义一个私有存储过程
test-#     BEGIN
test-#         RAISE NOTICE 'this is a private procedure!';
test-#     END;
test-#     PROCEDURE pubilc_procedure() AS    --定义包头中声明的公有存储过程
test-#     BEGIN
test-#         private_procedure();         --通过公有存储过程调用私有存储过程
test-#         RAISE NOTICE 'private var i = %',i;  --通过公有存储过程调用私有变量
test-#     END;
test-# BEGIN
test-#     i = 1;   --通过初始化块对私有变量进行初始化
test-# END;
test-# /
CREATE PACKAGE BODY


--在外部直接调用包的私有过程,报错找不到对应模式或者包下的过程
test=# call pkg5.private_procedure();
test-# /
ERROR:  schema or package "pkg5" does not exist
LINE 1: call pkg5.private_procedure();


--通过在外部调用包公有过程间接调用包的私有过程和变量,调用成功
test=# call pkg5.pubilc_procedure();
test-# /
NOTICE:  this is a private procedure!
NOTICE:  private var i = 1
CALL
复制代码


5. 更新包头后,无论是否对包体中的定义有影响,都需要重新编译包体才能正常调用

--创建一个包头,其中只声明了一个存储过程
test=# CREATE OR REPLACE PACKAGE pkg7 AS
test-#     PROCEDURE pt1();
test-# END;
test-# /
CREATE PACKAGE


--创建一个包体,定义包头中声明的存储过程
test=# CREATE OR REPLACE PACKAGE BODY pkg7 AS
test-#     PROCEDURE pt1() AS
test-#     BEGIN
test-#         RAISE NOTICE 'test';
test-#     END;
test-# END;
test-# /
CREATE PACKAGE BODY


--调用包中存储过程,可正常执行
test=# call pkg7.pt1();
test-# /
NOTICE:  test
CALL


--修改包头,增加一个变量的声明,对原有存储过程不改动
test=# CREATE OR REPLACE PACKAGE pkg7 AS
test-#     i int;
test-#     PROCEDURE pt1();
test-# END;
test-# /
CREATE PACKAGE


--再次调用包中存储过程,报错
test=# call pkg7.pt1();
test-# /
ERROR:  function (or procedure) "pt1" declared in package "pkg7" has not defined
LINE 1: call pkg7.pt1();


--重新编译包体,不做任何改动
test=# CREATE OR REPLACE PACKAGE BODY pkg7 AS
test-#     PROCEDURE pt1() AS
test-#     BEGIN
test-#         RAISE NOTICE 'test';
test-#     END;
test-# END;
test-# /
CREATE PACKAGE BODY


--再次调用包中存储过程,正确执行
test=# call pkg7.pt1();
test-# /
NOTICE:  test
CALL


--修改包体定义的存储过程中的执行内容,不需要重新编译包头
test=# CREATE OR REPLACE PACKAGE BODY pkg7 AS
test-#     PROCEDURE pt1() AS
test-#     BEGIN
test-#         RAISE NOTICE 'newtest';    --修改NOTICE内容
test-#     END;
test-# END;
test-# /
CREATE PACKAGE BODY


--调用包中存储过程,正常执行且为修改后的内容
test=# call pkg7.pt1();
test-# /
NOTICE:  newtest
CALL
复制代码


举例

为什么要使用包

在开发大型项目时会用到很多模块,每个模块都有自己的函数、存储过程、变量等元素,这些元素如果都放在一起,那么查询、维护起来就会很困难,这时候就需要使用包分类管理不同模块的元素。

假设一个场景,当我们需要做一个学校的管理系统,其中有需要对学生和教师进行评分的功能,对于学生和教师都有相似的行为,比如显示分数,修改分数等,但是其内部功能又不一样,需要不同的函数实现。如果创建几个名字相似的函数使用和管理起来比较容易混淆,而且后期需要移植也还需要确定哪些函数是相关的;这时候就可以通过创建两个不同的包,将相关的函数、变量都独立放在包里,每个包中的行为都很明确,而且移植起来只需要把整个包移植过去,使用和管理起来都很方便。

​​​​​​​包的应用场景

引用包中元素的方式有很多种,其中最基础的方式是:包名.元素名

创建管理学生信息的包 student,它具有从 stu 表中获取学生信息,显示学生信息,修改学生得分,和写回 stu 表的功能。

管理教师信息的包 teacher 创建也类似如下方式,只是根据情况修改包体中的实现。

--打开DBMS_OUTPUT信息输出
test=# set serverout on
--创建学生表并插入数据
test=# CREATE TABLE stu (id int primary key, name text, score number);
test-# insert into stu values (1, 'zs', 70);
test-# insert into stu values (2, 'ls', 80);
test-# insert into stu values (3, 'ww', 90);
test-# /
INSERT 0 1


--创建管理学生信息包的包头,声明管理功能需要的过程
test=# CREATE OR REPLACE PACKAGE student AS
test-#     PROCEDURE GET_STUDENT(t_id number);    --获取学生的信息
test-#     PROCEDURE SHOW_INFO();                --显示学生的信息
test-#     PROCEDURE CHANGE_SCORE(newscore NUMBER);   --修改学生的分数
test-#     PROCEDURE SAVE_STUDENT();              --保存学生的信息
test-# END;
test-# /
CREATE PACKAGE


--创建管理学生信息包的包体,声明一个私有变量存储学生信息,定义包头中声明的过程
test=# CREATE OR REPLACE PACKAGE BODY student AS
test-#     p_id int;        --私有变量,只用在包体过程定义中存储学生的信息
test-#     p_name text;
test-#     p_score number;
test-#     --将变量存储的学生信息打印出来
test-#     PROCEDURE SHOW_INFO AS
test-#     BEGIN
test-#         DBMS_OUTPUT.PUT_LINE('---student info---');
test-#         DBMS_OUTPUT.PUT_LINE('student id:'|| p_id);
test-#         DBMS_OUTPUT.PUT_LINE('student name:'|| p_name);
test-#         DBMS_OUTPUT.PUT_LINE('student score:'|| p_score);
test-#     END;
test-#
test-#     --从学生表中查出信息,存入变量中
test-#     PROCEDURE GET_STUDENT(t_id number) AS
test-#     BEGIN
test-#         SELECT id, name, score INTO p_id, p_name, p_score FROM stu WHERE id = t_id;
test-#         DBMS_OUTPUT.PUT_LINE('---successful get student info---');
test-#     END;
test-#
test-#     --改变变量的信息,未保存到表中
test-#     PROCEDURE CHANGE_SCORE(newscore number) AS
test-#     BEGIN
test-#         p_score = newscore;
test-#         DBMS_OUTPUT.PUT_LINE('---successful change student score---');
test-#     END;
test-#
test-#     --将变量中的信息更新保存到学生表中
test-#     PROCEDURE SAVE_STUDENT() AS
test-#     BEGIN
test-#         UPDATE stu SET score = p_score WHERE id = p_id;
test-#         DBMS_OUTPUT.PUT_LINE('---successful set student info---');
test-#     END;
test-# BEGIN
test-#     --初始化块,对包中变量进行初始化
test-#     p_id = 0;
test-#     p_name = '';
test-#     p_score = -1;
test-# END;
test-# /
CREATE PACKAGE BODY


--调用包中打印函数,打印学生的信息,当前还未获取学生的信息,打印的值为初始化值
test=# call student.SHOW_INFO();
test-# /
CALL
---student info---
student id:0
student name:
student score:-1


--调用包中获取学生信息函数,从学生表中获取信息存入包的私有变量
test=# call student.GET_STUDENT(1);
test-# /
CALL
---successful get student info---


--再次调用包中打印函数,打印当前获取的学生信息
test=# call student.SHOW_INFO();
test-# /
CALL
---student info---
student id:1
student name:zs
student score:70


--调用包中修改学生信息函数,传入需要修改的分数值
test=# call student.CHANGE_SCORE(99);
test-# /
CALL
---successful change student score---


--调用包中打印函数,打印修改过程的学生信息
test=# call student.SHOW_INFO();
test-# /
CALL
---student info---
student id:1
student name:zs
student score:99


--调用包中保存信息函数,将修改的学生信息更新到学生表中
test=# call student.SAVE_STUDENT();
test-# /
CALL
---successful set student info---


--查看学生表中对应学生信息,信息已改变
test=# select * from stu where id = 1;
test-# /
 id | name | score
----+------+-------
  1 | zs   |    99
(1 row)
复制代码


用户头像

金仓技术

关注

还未添加个人签名 2025-03-24 加入

还未添加个人简介

评论

发布
暂无评论
金仓数据库KingbaseES PAKCAGE的使用_KingBase_金仓技术_InfoQ写作社区