暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

金仓数据库KingbaseES PAKCAGE的使用

数据猿 2025-04-23
133

 


什么是包

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

包的组成

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

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

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

包的优点

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)


  

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论