现在你可以通过字符串建立索引提高性能
许多PL/SQL程序对数据进行操作通常是通过使用SQL在数据库中直接操作数据你经常还需要在PL/SQL程序本身内声明和管理数据这个程序数据可能由一些单独的值(标量)组成但是在其他许多情况下你可能要处理更复杂的数据结构从记录到对象(对象类型的实例)甚至是列表
列表(以及以更复杂的方式表示的数组)是程序员工具箱中最重要的元素为了创建和管理这些列表PL/SQL提供了各种各样称作集合的结构如嵌套表可变数组和关联数组
在Oraclei第版中引入的关联数组替代了索引表(它在Oracle中替代了PL/SQL表)关联数组引入了重要的新功能在Oraclei第版中你现在可以
定义在其内部包含其它集合的集合这些被叫做多层集合的列表使你能够在PL/SQL数据结构内部更加直观和直接地对现实世界的情况建模我在Oracle杂志年/月号的文章多层次的编程中介绍过这个特性
我们可以创建通过字符串来建立索引行的集合(并可快速进行检索)例如公司的名字在该集合中可以是行号
本篇文章说明怎样通过串来对关联数组建立索引和怎样定义这样的集合
用字符串来建立索引
有一个新的方法可用来定义和操作PL/SQL特定的集合特别是你除了使用行号这样的整数值来建立索引以外你还可以通过字符串来建立索引这个方法提供了相当重要的额外灵活性请看清单中的例子
在清单中第行声明了一个通过最大长度为个字符的串建立了索引的存储数字的关联数组类型第行和第行基于这个类型声明了个关联数组第行声明了一个变量来存储集合中的行号第行声明了一个变量来存储极限值(最低和最高的行索引值)请注意因为这个变量会接收一个基于串索引的值所以它被声明为一个字符串在第行和第行中我将值赋给了country_population每次我都把国家的人口数赋给集合中的一行那一行的索引就是它的国名在第行中我从集合中检索出一个值请注意我通过国名来确定那一行而脚本将返回相应的人口数从第行到第行我得到第一个定义的行值然后显示它接着显示那个国家的人口数数据库是怎样确定第一行的呢?第一行是通过数据库中字符集特定的排序顺序来确定的从第行到第行我使用对第一个定义的行值的相同的过程来得到最后一个定义的行值
对于字符串索引的集合你可能需要慢慢地习惯但是这种集合通过调用FIRSTLASTPRIOR和NEXT方法得到的返回值是字符串而不是数
字符串索引集合的使用
为什么你应该使用字符串而不是数来建立索引呢?假设你需要在你的程序中对员工的信息进行一些大量的处理你可能需反复地处理一组选定的雇员的信息例如按雇员的ID号码姓氏和社会保险号码(对于美国以外的一些国家或者是相应的身份证号码)来搜索员工
当然你可以使用SQL来完成这些任务但这远不是最高效的实现方式如果你需要多次处理一组数量很大的静态数据那么你可以改为把这组数据从数据库中移到一组集合中访问基于集合的数据比使用SQL引擎来访问数据要快得多
接下去你可以利用这些集合的基于字符串和基于整数的索引来模仿表(你已经把该表的数据从数据库中传出来了)的主键和特有的索引这个方法的一个简单的例子已在清单的代码中展示出
在清单中第行到第行声明了两个关联数组类型注意我可以在INDEX BY子句中利用%TYPE在其它的子句中使用PLS_INTEGER代替BINARY_INTEGER这些都是Oraclei Release 的新功能在第行到第行我声明了将用来提供进入数据的多个快速入口点的集合从第行到第行load_arrays过程显示了从数据库表将数据传送到一个或多个(在该实例中是个集合)是多么得容易我通过使用不同的字段值作为关键字把整行的雇员数据作为一个记录存放到每一个集合中在第行中我把姓氏用作索引值在第行中我使用社会安全号码作为索引值在第行中主关键字作为索引值(它是一个把整数索引作为一个智能关键字的相当传统的应用)在第行中我通过使用字符串和整数索引值对两个不同集合中的工资域进行比较
用字符串作索引的多层次集合
我已经创建了一个有趣的新实用程序叫做Codecheck它利用了 Oraclei第版集合的一些增强特性该程序包仔细审查ALL_ARGUMENT数据字典视图的内容以便分析特定的程序包和单独的应用程序其代码是否符合编码标准或揭示一些设计问题(例如不明确的超负荷)
ALL_ARGUMENTS包含关于每个存储在数据库(连接的用户在其上拥有EXECUTE权限中)的过程和函数的每个参数或自变量的信息ALL_ARGUMENTS中的单一行包含关于一个自变量(或者在某些情况下一个自变量的一个域(field)或一个元素)的信息下面是为ALL_ARGUMENT定义的字段的一个子集
名字 空值? 类型
OWNER 非空值 VARCHAR()
OBJECT_NAME VARCHAR()
PACKAGE_NAME
; VARCHAR()
OVERLOAD VARCHAR()
ARGUMENT_NAME VARCHAR()
POSITION 非空值 NUMBER
DATA_LEVEL 非空值 NUMBER
DATA_TYPE VARCHAR()
DEFAULT_VALUE LONG
IN_OUT VARCHAR()
很快你就会看到嵌入在这些行中的层次每个对象(OWNEROBJECT_NAME)可能超载(overload)(如果不超载或是一个负数则OVERLOAD是空值)overloading中的每一个自变量都有一个位置而在那个位置内你可以有自变量信息的多个层次换句话说
OBJECT_NAME
OVERLOAD
ARGUMENT_NAME
POSITION
LEVEL
由于篇幅所限我不可能提供Codecheck程序包中太多实用程序的实现无论如何我很高兴与您分享我为了轻松地快速分析ALL_ARGUMENTS的内容而把它的数据传送到我的集合中而写的代码
我在Codecheck程序包上进行第一次传送中我定义了一个可以简单地映射数据字典视图的集合类型和集合如下所示
CREATE OR REPLACE PACKAGE BODY Codecheck
IS
TYPE args_t IS TABLE OF
all_arguments%ROWTYPE
INDEX BY BINARY_INTEGER;
arguments args_t;
正如清单所示我通过使用定义的集合只使用了很少量的代码它就可以为特定的程序获得数据并把它插入到我的集合中
定义嵌套的集合
但是我在写完清单中的代码后马上意识到我正在以过时的方式思考问题我的计划是先填满我的集合接着写一些复杂的代码来扫描自变量集合然后查找一些事情诸如
一个程序包中特有程序的代号和名字我可能一共有个程序但由于超载因而只有个不同的程序名
顶级自变量项(其DATA_LEVEL = )
这就是在集合中用于标识一个程序结束和另一个程序开始的那些自变量
解决在ALL_ARGUMENTS视图中数据的组织问题并写出所需要的代码是我的任务
假如我定义多个嵌套的集合来存储这个数据将会怎样呢?也许通过这个方法集合的该组织可以使我更加容易地回答一些我的问题在经过思考和测试了一些可选方案之后我得到了如清单所示的集合类型的层次结构
图在ALL_ARGUMENTS集合映射中的四层嵌套
下面是对清单内容的描述像这样的情况最好是使用自底向上或者是从层次的最外层到最内部的集合的研究方法在第行中我声明了类型为programs_t的集合这个单独的集合将包含ALL_ARGUMENTS视图中的全部信息正像我的第一次尝试中那样但是该信息的组织却完全不同在第行和第行中我声明了集合类型programs_t这个类型的集合中的每一行包含有关一个给定程序名的全部overloadings的自变量的所有相关信息请注意索引是对象的名字在第行和第行中我声明了集合类型overloadings_t这个类型的集合中的每一行包含有关一个程序的一个单一overloading的自变量的所有相关信息现在我再次使用一个整数索引因为在这种情况下关键字是ALL_ARGUMENTS视图中的OVERLOAD字段的值在第行和第行中我声明了集合类型arguments_t这个类型的集合中的每一行包含一个特定overloading的一个单一参数或自变量的所有相关信息我还是使用一个整数索引因为在该情况下关键字是ALL_ARGUMENTS的POSITION字段的值(即参数列表中的位置)在第行和第行中我声明了集合类型breakouts_t这个类型的集合中的每一行包含一个自变量的一个单一元素的所有相关信息这可能是一行也可能是多行数据如果例如这个问题中的自变量是一个有个域的记录那么这个集合至少有行我借助一个整数索引因为在这个例子中关键字是ALL_ARGUMENTS视图中的LEVEL字段的值(在参数列表中表示实际的自变量)
是否令人有些困惑?首先不要只盯着ALL_ARGUMENTS视图中数据的复杂性我只想给你一个在我的代码中需要操作的变量的感受接下来我们看一下图中的图表注意在ALL_ARGUMENTS视图中嵌套的层次是怎样简单地以这些不同的层次来表达的
有了g_programs集合之后让我们再次查看如清单所示的Codecheck包中的load_arguments过程
清单中的load_arguments过程和清单中的版本有什么不同?整体地看非常小唯一不同的语句是在清单的第行到第行下面是在清单中的较早的简单赋值语句
arguments (
NVL (argumentsLAST ) + )
:= rec;
在清单中它变成了
g_programs
(recobject_name)
(recoverload)
(recposition)
(recdata_level) := rec;
这当然很复杂但它会为我处理那可怕的复杂性例如通过使用这个单一的赋值语句我实际上把全部个集合中的所有行放到该层次结构中当我完成这一工作后只花了很少的代码就满足了我的检索数据的需要例如获得TOTAL_SALES函数的第个overloading的RETURN子句的数据类型
g_programs (TOTAL_SALES) locate
() second overloading
() RETURN clause has position
() Toplevel argument
data_type Value in the
data_type field
在这种情况下使用这个语法比编写和调试扫描一个平面(flat)(Oraclei数据库之前)集合所需要的逻辑来得到这样的信息代码要容易得多
当然我并不期望你能立即完全领会这些复杂的集合结构的潜在能力我只希望这个段落的一些例子可以起到抛砖引玉的作用
这么多的选择!
只要适当地使用集合将帮助我们编写出更高效和更易懂的代码将多层次的编程和用字符串建立索引可以帮助我们减少复杂性和减少建模和操作来自复杂数据库设计的信息所需的编码量