Oracle PL/SQL变得更快更易于使用特性集也更加丰富了Oracle数据库g通过一系列有益的改进继续保持了PL/SQL在速度简单易用性和特性扩展方面的传统优势这些改进包括
大大提高了执行速度这要归功于透明的性能改进其中包括一个新的优化编译器更优的集成化本地编译功能以及帮助解决数字运算应用程序问题的新的数据类型
FORALL语句更加灵活更加有用例如FORALL现在支持非连续索引
正则表达式以三个新函数(REGEXP_INSTRREGEXP_REPLACE和REGEXP_SUBSTR)和用于比较的REGEXP_LIKE运算符的形式用于PL/SQL语言中(要获得更多信息参见本期杂志中Jonathan Gennick撰写的《一流的表达式》一文)
集合得到了改进包括比较集合是否相同支持对嵌套表进行集合运算等
Oracle数据库g使PL/SQL继续保持其作为Oracle数据库的最高效最富生产力的编程语言的地位其在性能方面的大大提升以及对IEEE算法和正则表达式的支持如今完全开启了将PL/SQL作为首选语言的新的功能领域
作为介绍Oracle数据库g中的PL/SQL系列文章的第一篇本文将对Oracle数据库g中与集合相关的一些改进进行探讨
比较集合
世纪年代末期当我首次对开发人员进行PL/SQL培训的时候很少有人使用(甚至知道)包(package)而现在很多人(包括我自己在内)都认为它是任何设计精良的PL/SQL应用程序的基础如今打包软件得到了广泛使用 目前非常重要但未得到充分利用的PL/SQL特性的前沿领域似乎就是集合的使用了
集合是Oracle中的数组集合是一维列表早在Oracle中就已经用到了集合(那时称作PL/SQL表)但其功能及性能却很有限不过Oracle后来的各个版本都对集合进行了改进在Oracle数据库g中这些数据结构对于几乎所有的复杂PL/SQL应用程序项目来说都是强有力的快速的和必不可少的
Oracle数据库g对集合的一个关键性改进是能够比较两个集合内容的相同之处(和不同之处)在Oracle数据库g之前你也可以对两个集合进行比较但必须为此编写一个函数编写这样的程序时需要考虑数个复杂因素包括
必须为正在使用的各个集合类型分别编写程序即使两个集合中的数据类型相同但如果它们不是基于完全相同的类型定义的你都需要使用不同的函数来进行比较
必须对表的内容进行逐行比较这就意味着必须进行全集合扫描完成这一任务的代码不是很复杂但是这一代码冗长乏味易于出错特别是比较诸如记录对象或者XMLType等复杂数据类型的集合时更是如此
你必须决定如何处理NULL如果两行都包含NULL那么它们相同吗?Oracle认为它们既不是相同也不是不相同但是你的判断可能会与此不同你必须编写代码来处理这个问题
这种复杂性导致你不愿意经常编写这类程序你甚至会在应用程序中回避编写这类程序
在Oracle数据库g中collcomparesql文件包含了这类程序的一个示例该程序是为基于employee表的记录集合而编写的
假设我在collcomparesql脚本中安装了emp_coll_pkgequal函数我可以按照如下方式来使用它
DECLARE
dbas emp_coll_pkgemployee_tt;
developers emp_coll_pkgemployee_tt;
BEGIN
populate_lists (dbas developers);
IF emp_coll_pkgequal (dbas developers)
THEN
DBMS_OUTPUTPUT_LINE (
Likely a very small IT organization!);
END IF;
END;
这段代码简单明了可读性好(你不用再经历编写此类函数的痛苦过程多好啊!)为了让你工作更轻松Oracle数据库g现在允许你对两个嵌套表进行原始比较(native compare)了换句话说你不必再编写任何特定于集合的比较逻辑而可以直接进行比较如下所示
DECLARE
dbas emp_coll_pkgemployee_tt;
developers emp_coll_pkgemployee_tt;
BEGIN
populate_lists (dbas developers);
IF dbas = developers
THEN
DBMS_OUTPUTPUT_LINE (
Likely a very small IT organization!);
END IF;
END;
在这种情况下集合比较仅适用于嵌套表换句话说你还不能直接比较两个关联数组(过去称作索引表)或者变长数组的内容希望Oracle数据库的下一版本会增加关联数组和变长数组的比较功能
集合理论与Multiset Union运算
SQL语言很久以前就提供了将集合运算(UNIONINTERSECT和MINUS)用于查询结果集的功能如今在Oracle数据库g中你可以对PL/SQL程序中的嵌套表(且仅限于嵌套表)和在关系表中声明为列的嵌套表使用上述功能强大的高级运算符
现在我们从UNION开始看看这样做所需的一些语法
首先我创建一个模式级别的嵌套表类型
CREATE OR REPLACE TYPE strings_nt
IS TABLE OF VARCHAR();
/
然后我定义一个包在该包中我创建并填充两个此种类型的嵌套表每个嵌套表都包含一些我和我父亲最喜欢的东西
CREATE OR REPLACE PACKAGE favorites_pkg
IS
my_favoritesstrings_nt
:= strings_nt (CHOCOLATE
BRUSSEL SPROUTS
SPIDER ROLL
);
dad_favorites strings_nt
:= strings_nt (PICKLED HERRING
POTATOES
PASTRAMI
CHOCOLATE
);
PROCEDURE show_favorites (
title_in IN VARCHAR
favs_inIN strings_nt
);
END;
/
在该包中我还创建了一个用于显示strings_nt 嵌套表内容的过程下面很快就会用到它
通过在任意程序外的包中定义这些集合这些集合在我的对话期间会一直保持不变(保持其状态和值)直到我将更改或删除它们为止这就是说现在我可以在包外编写程序来对这些集合的内容进行操作了
注意出于介绍集合功能的目的我对该包进行了简化在生产应用程序中你应该时刻注意隐藏包主体中的包数据(如同这些集合一样)然后提供过程和函数来管理这些数据
例如假设我想把这两个集合合并成一个我们的最爱的集合在Oracle数据库g出现以前我必须编写一个将一个集合的内容转移到另一个集合的循环而现在我可以依赖MULTISET UNION运算符来实现这一点了如下所示
DECLARE
our_favorites
strings_nt := strings_nt ();
BEGIN
our_favorites :=
favorites_pkgmy_favorites
MULTISET UNION
favorites_pkgdad_favorites;
favorites_pkgshow_favorites (
ME then DAD our_favorites);
END;
/
此脚本的输出结果为
ME then DAD
= CHOCOLATE
= BRUSSEL SPROUTS
= SPIDER ROLL
= PICKLED HERRING
= POTATOES
= PASTRAMI
= CHOCOLATE
可以看出两个嵌套表的值被合并到一起了而且立刻就可以看到MULTISET UNION运算符不同于SQL UNION运算符(实际上与SQL的UNION ALL运算符完全相同)对两个SELECT结果集进行UNION运算时SQL引擎会自动生成一个惟一的有序结果集换句话说如果我的两个嵌套表都是查询那么UNION将生成这样的结果集
BRUSSEL SPROUTS
CHOCOLATE
PASTRAMI
PICKLED HERRING
POTATOES
SPIDER ROLL
这些数据是按照字母顺序排列的且CHOCOLATE仅出现一次为什么会产生不同的结果呢?因为嵌套表是一种多集合(multiset)m/l 对其做了如下定义
一种类似于集合的对象在其内部顺序没有意义而多重性却具有明确的意义因此多集合{ }和{ }是等同的而{ }和{ }却是不同的
Oracle文件中说嵌套表和变长数组的区别在于嵌套表列中存储的数据不保存其顺序而在变长数组中存储的数据保存其顺序在Oracle数据库g之前这种区别在PL/SQL中没多大意义现在有了集合运算符之后多集合(或嵌套表)的特性便显得极为重要了
为了更好地理解嵌套表数据没有顺序MULTISET UNION也不会对结果嵌套表应用顺序这一特性请我们来看看面的这个程序块
DECLARE
our_favorites
strings_nt := strings_nt ();
BEGIN
our_favorites :=
favorites_pkgdad_favorites
MULTISET UNION
favorites_pkgmy_favorites;
favorites_pkgshow_favorites (
DAD then ME our_favorites);
END;
/
与前面的程序块惟一不同的是我改变了MULTISET UNION运算中嵌套表的顺序结果就变为
DAD then ME
= PICKLED HERRING
= POTATOES
= PASTRAMI
= CHOCOLATE
= CHOCOLATE
= BRUSSEL SPROUTS
= SPIDER ROLL
如果你不希望合并之后的嵌套表中出现重复项那么可