#自定义高级包
自定义高级包是数据库里的一种PL对象,简称UDP。
分布式部署中不能创建和使用UDP。
# 创建UDP
UDP的创建分为两部分:
- 定义PACKAGE HEAD:PACKAGE HEAD用于声明PUBLIC属性的变量、类型、游标和子过程体对象(存储过程、自定义函数),在UDP被成功创建后,这些变量、类型、游标和子过程体对象将可以作为UDP的成员被其他外部程序所引用。
- 定义PACKAGE BODY:PACKAGE BODY用于定义过程体,PACKAGE BODY除可以直接使用PACKAGE HEAD中声明的变量外,也可以自行声明新的变量,但这些变量为该UDP私有(PRIVATE),即只能在PACKAGE BODY中使用,而不可以被外部其他过程体所引用。
PACKAGE HEAD和PACKAGE BODY通过不同语法分别定义,且YashanDB并不严格要求HEAD和BODY的创建顺序,如在HEAD还未创建时先定义BODY,系统将仍然创建该UDP,但会因为HEAD声明不存在而抛出编译错误,该UDP也无法被执行和调用,直到HEAD被创建。
系统对PACKAGE HEAD和PACKAGE BODY还存在如下约束要求:
- HEAD和BODY内部不允许出现同名的变量或者子过程体对象声明;
- HEAD中的子过程体对象声明,在BODY中必须有对应的过程体定义,在HEAD中声明的参数缺省值在BODY中可以省略;
- 对于子过程体对象,HEAD中只能对其进行声明,BODY中只能对其进行过程体定义。
使用CREATE PACKAGE语句可以分别创建PACKAGE HEAD或者PACKAGE BODY,语法定义如下:
create package::=
package_head_clause::=
invoker_rights_clause::=
pragma_clause::=
package_item_clause::=
pl_declare::=
package_body_clause::=
package_subprogram_clause::=
# or replace
当要创建的UDP已经存在时,将其进行重建。
# editionable | noneditionable
用于语法兼容,无实际含义。
# package_name
UDP的名称,不可省略,且需符合YashanDB的对象命名规范。
# package_head_clause
创建一个UDP的HEAD。
# invoker_rights_clause
见PL对象中invoker_rights_clause部分描述。
# pragma_clause
可选项,表示UDP的变量每次被引用时,是否将其重置为变量声明时定义的缺省值,默认不重置。
在YashanDB里,在UDP中声明的变量均为SESSION级别的全局变量,这意味着在同一个会话中,变量赋值相互影响,而不同会话中的变量则相互独立。通过设置SERIALLY_REUSABLE可以在每次引用变量(客户端到服务端的一次调用)时让其恢复初始值。
示例(单机、共享集群部署)
DROP PACKAGE IF EXISTS calc_fee;
CREATE OR REPLACE PACKAGE calc_fee
AS
c NUMBER := 100;
PROCEDURE branch_quantity(date_from date);
END;
/
CREATE OR REPLACE PACKAGE BODY calc_fee AS
PROCEDURE branch_quantity(date_from DATE) IS
BEGIN
DBMS_OUTPUT.PUT_LINE(c);
c := c+1;
DBMS_OUTPUT.PUT_LINE(c);
END;
END calc_fee;
/
--第一次执行
exec calc_fee.branch_quantity(sysdate);
100
101
--第二次执行
exec calc_fee.branch_quantity(sysdate);
101
102
--设置pragma_clause
CREATE OR REPLACE PACKAGE calc_fee
AS
PRAGMA SERIALLY_REUSABLE;
c NUMBER := 100;
PROCEDURE branch_quantity(date_from date);
END;
/
CREATE OR REPLACE PACKAGE BODY calc_fee AS
PRAGMA SERIALLY_REUSABLE;
PROCEDURE branch_quantity(date_from date) IS
BEGIN
DBMS_OUTPUT.PUT_LINE(c);
c := c+1;
DBMS_OUTPUT.PUT_LINE(c);
END;
END calc_fee;
/
--第一次执行
exec calc_fee.branch_quantity(sysdate);
100
101
--第二次执行
exec calc_fee.branch_quantity(sysdate);
100
101
# package_item_clause
UDP的成员声明,包括变量声明和子过程体对象声明,一个UDP(HEAD或BODY)里可声明的成员数量最多为1024个。
- 变量声明:支持在声明章节中列出的各类变量的声明,但对于游标,允许显式定义游标,不允许显式声明游标。可交叉声明多个不同类变量且顺序无限制。
- 子过程体对象声明:支持声明存储过程和自定义函数。允许交叉声明多个存储过程和自定义函数,且顺序无限制。
变量声明必须位于子过程体对象声明的前面,且定义的成员先后顺序决定了后一个成员是否可见前面成员。
# package_body_clause
创建一个UDP的BODY。
# package_subprogram_clause
UDP里的过程体定义,包括PROCEDURE或FUNCTION的定义,定义语法同存储过程和自定义函数章节里的过程体语法描述。
# 加载udp的变量
UDP的变量无法在外部SQL语句中直接访问,而是通过在过程体(内部或外部)中加载使用,加载格式为:
[schema.][package_name.]variable_name;
在被外部过程体调用时,package_name不可省略。
在YashanDB中,UDP的变量与UDP进行绑定管理,如果UDP的HEAD或者BODY发生变更,该UDP的所有变量将被重置。此规则与Oracle数据库存在差异。
# 运行udp的子过程体对象
SQL语句中调用子函数
通过SQL语句调用UDP的子函数,允许出现在SQL语句的列项、条件项或查询选项(如ORDER BY/LIMIT/OFFSET)等各个位置。以出现在查询列为例的调用格式为:
SELECT package_name.subpl_name[(arguments)] FROM table_name;
CALL/EXEC语句中调用子存储过程
通过CALL/EXEC语句可调用UDP的子存储过程,调用格式为:
(CALL|EXEC) package_name.subpl_name[(arguments)];
过程体中调用子存储过程
通过在过程体(内部或外部)中调用子存储过程,调用格式为:
[package_name.]procedure_name[(arguments)];
在被外部过程体调用时,package_name不可省略。
示例(单机、共享集群部署)
--创建无HEAD的UDP
DROP PACKAGE IF EXISTS calc_fee;
--创建UDP的BODY,由于未声明相关变量会返回错误,实际上该BODY已创建成功
CREATE OR REPLACE PACKAGE BODY calc_fee AS
PROCEDURE calc_rev(prefix CHAR) IS
str1 VARCHAR(100) := 'select sum(revenue_total) from finance_info where substr(branch,0,2)=:a';
BEGIN
EXECUTE IMMEDIATE str1 INTO c USING prefix;
DBMS_OUTPUT.PUT_LINE(c);
END;
END calc_fee;
/
--result
YAS-05278 cannot compile body without its specification
--创建calc_fee的HEAD(此步骤后无需再次创建BODY)
CREATE OR REPLACE PACKAGE calc_fee
AS
c NUMBER := 100;
PROCEDURE calc_rev(prefix CHAR);
END calc_fee;
/
--调用UDP
DECLARE
c CHAR(2);
BEGIN
c := '01';
DBMS_OUTPUT.PUT_LINE(c);
DBMS_OUTPUT.PUT_LINE(calc_fee.c);
calc_fee.calc_rev(c);
END;
/
--result
01
100
76666
# 删除自定义高级包
使用DROP PACKAGE语句删除自定义高级包。
示例(单机、共享集群部署)
--创建UDF,调用上例中的UDP子过程体对象
CREATE OR REPLACE FUNCTION calc(c CHAR) RETURN NUMBER IS
BEGIN
calc_fee.calc_rev(c);
RETURN calc_fee.c;
END;
/
SELECT calc('01') FROM dual;
CALC('01')
-----------
76666
--删除UDP
DROP PACKAGE calc_fee;
SELECT calc('01') FROM dual;
[1:8]YAS-04253 PL/SQL compiling errors:
[3:3] YAS-04243 invalid identifier "CALC_FEE"."CALC_REV"
[4:10] YAS-04243 invalid identifier "CALC_FEE"."C"
# 修改自定义高级包
使用ALTER PACKAGE语句修改自定义高级包。