将代码封装在灵巧的包中
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. 将PL/SQL代码封装在机灵的包中

    将代码封装在机灵的包中 http://www.oracle.com/technetwork/issue-archive/2013/13-jan/o13plsql-1872456.html 绝大多数基于 ...

  2. 同样的一句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 ...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. redis-持久化-RDB-AOF.png

  2. [转帖]CIDR

    什么是 CIDR? 无类别域间路由 (CIDR) 是一种 IP 地址分配方法,可提高互联网上的数据路由效率.每台连接到互联网的计算机.服务器和最终用户设备都有一个与之关联的唯一编号,称为 IP 地址. ...

  3. [转帖]PD 配置文件描述

    https://docs.pingcap.com/zh/tidb/stable/pd-configuration-file PD 配置文件比命令行参数支持更多的选项.你可以在 conf/config. ...

  4. Jmeter学习之四_kingbaseV8R6数据库的简单验证

    Jmeter学习之四_kingbaseV8R6数据库的简单验证 背景 周一没去报道, 因为我忘记体检了... 继续在家进行学习提高自己. jmeter周末时开始看的. 今天想着继续研究一下对数据库的处 ...

  5. vue同步组件和异步组件的区别

    异步组件 异步组件:只在组件需要渲染(组件第一次显示)的时候进行加载渲染并缓存,缓存是以备下次访问. Vue实现按需加载 在打包的时候,会打包成单独的js文件存储在static/js文件夹里面** 在 ...

  6. Linux命令-tail命令

    Linux命令-tail 命令分析 命令格式 参数 例子 Linux命令-tail 命令分析 tail命令可用于查看文件的内容,通常用来查看日志,加上-f参数就可以查看最新的日志并且不断刷新. 命令格 ...

  7. 基于知识图谱的电影知识问答系统:训练TF-IDF 向量算法和朴素贝叶斯分类器、在 Neo4j 中查询

    基于知识图谱的电影知识问答系统:训练TF-IDF 向量算法和朴素贝叶斯分类器.在 Neo4j 中查询 1.项目介绍 训练 TF-IDF 向量算法和朴素贝叶斯分类器,预测用户文本所属的问题类别 使用分词 ...

  8. 4.1 C/C++ 使用结构与指针

    C/C++语言是一种通用的编程语言,具有高效.灵活和可移植等特点.C语言主要用于系统编程,如操作系统.编译器.数据库等:C语言是C语言的扩展,增加了面向对象编程的特性,适用于大型软件系统.图形用户界面 ...

  9. SpringCloud-05-Gateway

    统一网关Gateway 1.为什么需要网关 网关功能: 身份认证和权限校验 服务路由.负载均衡 请求限流 2.网关的技术实现 在SpringCloud中网关的实现包括两种: gateway zuul ...

  10. SpringCloud-04-http客户端Feign

    http客户端Feign 1.Feign的介绍 Feign是一个声明式的http客户端,官方地址:https://github.com/OpenFeign/feign 其作用就是帮助我们优雅的实现ht ...