将代码封装在机灵的包中

http://www.oracle.com/technetwork/issue-archive/2013/13-jan/o13plsql-1872456.html

绝大多数基于PL/SQL的应用都是由成千上万甚至上百万行代码组成,这里面包括了具体多变的用户需求。

商业逻辑的实现最初是由存储过程和函数完毕,可是开发人员须要考虑将这些过程和函数放在包中维护。

何为包?

包是一组PL/SQL代码元素(游标、类型、变量、过程、函数)集合的程序单元。

通常由包声明(对象声明)和包体(具体实现)组成。

为什么要使用包?

1)组织和维护一组功能相关的对象;

2)对外隐藏具体实现;

3)提升性能。这一点要说一下:

当你第一次调用包时。整个包被载入入内存。接下来对同一包元素进行调用无需额外的磁盘I/O。

另外,包级别变量能够再会话级别(session-level)缓存起来。从而减少数据读取时间。

4)最小化程序单元重编译

外部程序(未定义在包中)仅能调用包声明中的子程序。假设你改变并又一次编译了包体。那些外部程序

将不会失效。

以下展示一下包的魅力:

1 一个简单的包:

假设我的employees表定义例如以下:

SQL> desc employees

Name             Type
———————————— —————————————
EMPLOYEE_ID NUMBER(38)
FIRST_NAME VARCHAR2(30)
LAST_NAME VARCHAR2(50)

以下我须要定义一个process_employee的过程。返回员工全名(last_name, first_name)以供其它

程序调用。

Code Listing 1: The process_employee procedure

CREATE OR REPLACE PROCEDURE process_employee (
employee_id_in IN employees.employee_id%TYPE)
IS
l_fullname VARCHAR2(100);
BEGIN
SELECT last_name || ',' || first_name
INTO l_fullname
FROM employees
WHERE employee_id = employee_id_in;
...
END;

细致看。这个过程有几个问题:

1)l_fullname 长度固定为100?

2)l_fullname的表达式固定为 last_name || ‘,’ || first_name?万一哪天客户改变主意:

我们想在全部报告和信息中显示:first_name【空格】last_name咋办?假设你在N个过程中都已经

使用了这样的结构。那你是不是去一一找出来改动掉?

3)最后一点,我们非常有可能在不同的过程中编写一些反复SQL,这样会大大减少效率和性能

这个时间,我们须要将这样的通用逻辑藏在包中,保证一处维护处处受益:

CREATE OR REPLACE PACKAGE employee_pkg
2 AS
3 SUBTYPE fullname_t IS VARCHAR2 (100);
4
5 FUNCTION fullname (
6 last_in employees.last_name%TYPE,
7 first_in employees.first_name%TYPE)
8 RETURN fullname_t;
9
10 FUNCTION fullname (
11 employee_id_in IN employees.employee_id%TYPE)
12 RETURN fullname_t;
13 END employee_pkg;

回头再改写过程,能够这样:

CREATE OR REPLACE PROCEDURE process_employee (
employee_id_in IN employees.employee_id%TYPE)
IS
l_name employee_pkg.fullname_t;
employee_id_in employees.employee_id%TYPE := 1;
BEGIN
l_name := employee_pkg.fullname (employee_id_in);
...
END;

代码变整洁了,还有你压根不须要关心employee_pkg.fullname 怎样实现!多省心。

来看下包体是怎样实现的:

CREATE OR REPLACE PACKAGE BODY employee_pkg
2 AS
3 FUNCTION fullname (
4 last_in employees.last_name%TYPE,
5 first_in employees.first_name%TYPE
6 )
7 RETURN fullname_t
8 IS
9 BEGIN
10 RETURN last_in || ', ' || first_in;
11 END;
12
13 FUNCTION fullname (employee_id_in IN employee.employee_id%TYPE)
14 RETURN fullname_t
15 IS
16 l_fullname fullname_t;
17 BEGIN
18 SELECT fullname (last_name, first_name) INTO l_fullname
19 FROM employees
20 WHERE employee_id = employee_id_in;
21
22 RETURN l_fullname;
23 END;
24 END employee_pkg;

这里用到了函数重载。使得外部过程仅仅须要传入不同參数就可以调用不同版本号的函数。

终于都会返回fullname。

2 包级别数据

此类数据由包声明和包体中全局的variables 和 constants组成。

比如:

CREATE OR REPLACE PACKAGE plsql_limits
IS
c_varchar2_length CONSTANT
PLS_INTEGER := 32767;
g_start_time PLS_INTEGER;
END;

当你在一个子程序或匿名块中声明一个变量。称为本地变量。其声明周期限制在一次子程序调用或匿名块运行。

而包级别数据是在整个会话期间都会存活。

假设你在包体中定义包数据(变量和常量),该数据相同在会话期间存活,可是这类数据仅仅能被包中程序使用。即为私有数据。

还有一方面。假设是在包声明中定义包数据则对全部具有运行包权限的程序都可使用。

来看一个样例:

DBMS_UTILITY包中GET_CPU_TIME函数可用来计算你的程序耗时

Code Listing 5: DBMS_UTILITY.GET_CPU_TIME measures

DECLARE
l_start PLS_INTEGER;
BEGIN
/* Get and save the starting time. */
l_start := DBMS_UTILITY.get_cpu_time; /* Run your code. */
FOR indx IN 1 .. 10000
LOOP
NULL;
END LOOP; /* Subtract starting time from current time. */
DBMS_OUTPUT.put_line (
DBMS_UTILITY.get_cpu_time - l_start);
END;
/

看着足够简单了吧,可是你还是须要声明一个本地变量来存放耗时!

so,我们有更快捷的方式,使用自己定义包timer_pkg!!!

Code Listing 6: The timer_pkg package

CREATE OR REPLACE PACKAGE timer_pkg
IS
PROCEDURE start_timer; PROCEDURE show_elapsed (message_in IN VARCHAR2 := NULL);
END timer_pkg;
/ CREATE OR REPLACE PACKAGE BODY timer_pkg
IS
g_start_time NUMBER := NULL; PROCEDURE start_timer
IS
BEGIN
g_start_time := DBMS_UTILITY.get_cpu_time;
END; PROCEDURE show_elapsed (message_in IN VARCHAR2 := NULL)
IS
BEGIN
DBMS_OUTPUT.put_line (
message_in
|| ': '
|| TO_CHAR (DBMS_UTILITY.get_cpu_time - g_start_time)); start_timer;
END;
END timer_pkg;
/

改写之前的匿名块,例如以下:

BEGIN
timer_pkg.start_timer;
FOR indx IN 1 .. 10000
LOOP
NULL;
END LOOP;
timer_pkg.show_elapsed ('10000 Nothings');
END;
/

哇哦!

good job!

不再须要声明本地变量,不再须要理解GET_CPU_TIME function 怎样工作!

3 子程序重载

我们都知道DBMS_OUTPUT.PUT_LINE用于往控制台打印字符数据。

BEGIN
DBMS_OUTPUT.PUT_LINE (100);
END;

其有一个弊端,仅仅能输出字符类型!

SQL> BEGIN
2 DBMS_OUTPUT.PUT_LINE (TRUE);
3 END;
4 /
DBMS_OUTPUT.PUT_LINE (TRUE);
*
ERROR at line 2:
ORA-06550: line 2, column 4:
PLS-00306: wrong number or types of
arguments in call to ‘PUT_LINE’

多尴尬。 比較BOOLEAN类型无法转成字符类型!

非常多开发人员不得不这么搞:

IF l_student_is_registered
THEN
DBMS_OUTPUT.PUT_LINE ('TRUE');
ELSE
DBMS_OUTPUT.PUT_LINE ('FALSE');
END IF;

不得不说精神可嘉!

可是。我们有更好的方式:

Code Listing 7: The my_output package without overloading

CREATE OR REPLACE PACKAGE my_output
IS
PROCEDURE put_line (value_in IN VARCHAR2); PROCEDURE put_line (value_in IN BOOLEAN); PROCEDURE put_line (
value_in IN DATE,
mask_in IN VARCHAR2 DEFAULT 'YYYY-MM-DD HH24:MI:SS');
END my_output;
/

这就充分发挥了重载的价值!

4 包状态及ORA-04068错误

这个问题是不论什么开发包的人都无法回避的。

包有状态?

当一个包有至少一个常量或变量声明在包级别,该包就有了状态!

当一个会话调用有状态包,PGA将包全部包级别数据存储起来。

假设一个状态包又一次编译,全部使用该包的会话在下次调用时都会抛出:ORA-04068错误。

由于存储在PGA中包级别数据已经过期了(out of date)!所以包必须再次初始化。

此外。一旦ORA-04068抛出。会话中全部状态包,比如,DBMS_OUTPUT都将标识为未初始化。

这通常意味着用户

必须断开会话又一次连接。

这个潜在的错误意味着当IT部门须要升级应用,他们须要确保全部用户已注销。

可是在7*24的互联网世界这是

不能容忍的。

所以在Oracle 11g r2中,oracle提供了基于版本号的重定义功能(Edition-Based Redefinition feature)。

具体请參考:oracle.com/technetwork/database/features/availability/edition-based-redefinition-1-133045.pdf and docs.oracle.com/cd/E11882_01/appdev.112/e10471/adfns_editions.htm

将PL/SQL代码封装在机灵的包中的更多相关文章

  1. 同样的一句SQL语句在pl/sql 代码块中count 没有数据,但是直接用SQl 执行却可以count 得到结果

    pl/sql 代码块: SELECT count(distinct t2.so_nbr) INTO v_count2 FROM KFGL_YW_STEP_qd t2 WHERE t2.partitio ...

  2. 使用PL/Scope分析PL/SQL代码

    使用PL/Scope分析你的PL/SQL代码 从11g開始Oracle引入了PL/Scope 用于编译器收集PL/SQL程序单元的全部标识符(变量名.常量名.程序名等). 收集到的信息可通过一系列静态 ...

  3. plsql programming 20 管理PL/SQL代码(个人感觉用不到)

    这一章的内容, 只完成了一部分, 剩下的用到再补充吧 由于依赖关系, 而编译失败, 需要重新编译. ( 所谓依赖, 是指存储过程, 函数等在运行中调用的对象, 比如table 等, 比如你删除了过程中 ...

  4. oracle 中使用 pl/sql代码块

    1.写匿名块,输入三角形三个表的长度.在控制台打印三角形的面积. declare -- (p=(a+b+c)/2) --声明三角形的面积 三条边 的 v_a number (10,2):=&n ...

  5. PL/SQL:使用pragma restrict_references限制包权限

    在看别人的代码的时候.发现了例如以下的编译指令. pragma restrict_references(get_attribute_name, wnds); get_attribute_name是一个 ...

  6. PL/SQL 编程(三 )程序包和包体,触发器,视图,索引

    一.程序包和包体 程序包(package):存储在数据库中的一组子程序.变量定义.在包中的子程序可以被其它程序包或子程序调用.但如果声明的是局部子程序,则只能在定义该局部子程序的块中调用该局部子程序. ...

  7. 【Oracle】PL/SQL Developer使用技巧(持续更新中)

    1.关键字自动大写 在sql命令窗口中输入SQL语句时,想要关键字自动大写,引人注目该怎么办呢? 一步设置就可以达成了.点击Tools->Preference->Editor,看到截图中这 ...

  8. 解决PL/SQL Developer 连接oracle 11g 64位中的问题

    1.错误1:Initialization error could not initialize 电脑上原本就装有oracle 11g 64位,但是PL/SQL却怎么也连接不上,报出" Ini ...

  9. pl/sql学习(6): 引号/程序调试/列中的字符串合并/正则表达式

    有关自治事务的问题: https://www.cnblogs.com/princessd8251/p/4132649.html 我在plsql development学习中遇到的常见问题: (一) 引 ...

随机推荐

  1. 关于OJ的输入和输出(转)

    ACM竞赛之输入输出以下内容来源于互联网.在ACM程序设计竞赛中,一道题目的所有测试数据是放在一个文本文件中,选手将一道题目的程序提交给评判系统运行,程序从该文件中读取测试数据,再把运行结果输出到另一 ...

  2. cmd下运行java文件时,找不到或无法加载主类的解决方法

    最近再看java,却被一个看似很基础的hellorworld头疼了十几分钟,百度了一下,若你在cmd下编辑及运行.java文件时报错,解决方案如下,如图所示:

  3. 标签form表单里的属性简单介绍了一下

    <form id="form1" name="form1" method="post" action=""> ...

  4. TFS环境搭建

    这篇文章主要介绍了微软源代码管理工具TFS2013安装与使用图文教程,本文详细的给出了TFS2013的安装配置过程.使用教程,需要的朋友可以参考下 最近公司新开发一个项目要用微软的TFS2013进行项 ...

  5. 加JENKINS的SLAVE节点(LINUX)要注意的事项

    从昨天下午到现在,终于解决了所有的问题.作如下记录要点: 1,从节点只要建好目录,JENKINS的包,MASTER会推送过来. 2,ANT,MAVEN之类的,要配置好环境变量,PROFILE.D .B ...

  6. String.format 细节

    String.format(Locale.US, format, args); format 参数 如果有% 那么%后面必须跟一个合法的字符,否则崩溃, 因为在String.format中 %为特殊字 ...

  7. XP中IIS“HTTP 500 - 内部服务器错误”解决方法

    我先把主要过程叙述一下,叙述完有每个问题的具体操作方法. 今天我在XP上安装IIS,运行网站出现"HTTP 500 - 内部服务器错误". 打开HTML没有问题,打开ASP文件时就 ...

  8. c语言开源项目--SQLite学习资料总结

    同行博客: 1.http://www.cnblogs.com/hustcat/category/175618.html; 2.http://blog.csdn.net/zhoudaxia/articl ...

  9. HDU 5963 朋友 【博弈论】 (2016年中国大学生程序设计竞赛(合肥))

    朋友 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Problem Descr ...

  10. Uninstall Tool 3.3.2.5315 简体中文注册版(彻底卸载软件)

    Uninstall Tool Uninstall Tool是CrystalIdea Software出品的一款强大而灵活的Windows标准“添加/删除程序”工具的替代软件.它能快速,安全而方便的删除 ...