联合数组的语法如下:
|
TYPE table_type_name IS TABLE OF
datatype [ NOT NULL ]
INDEX BY
index_type; |
我们只看一下
index_type,其它的比较简单。
Oracle9i Database Release 2 之前,只能使用
binary_integer 作为索引下标。从 Oracle9i Release 2 开始,可以使用 binary_integer
及其所有的子类型、varchar( n ),或者 %type 锚定的列或变量(当然,也必须是这两种类型之一)。也就是说,现在支持的索引类型如下:
|
INDEX BY
BINARY_INTEGER;
INDEX BY
PLS_INTEGER;
INDEX BY
POSITIVE;
INDEX BY
NATURAL;
INDEX BY
SIGNTYPE;
INDEX BY
VARCHAR2(32767);
INDEX BY
table.column%TYPE;
INDEX BY
cursor.column%TYPE;
INDEX BY
package.variable%TYPE;
INDEX BY
package.subtype; |
1. VARCHAR2
下标
有时候,使用 varchar2
类型的索引来存储数据,会使我们的编码变得更加简洁。我们举个小例子,来看一下它的用法:
|
SQL> set serveroutput
on
SQL>
declare
2
type taa_salary is table of number(10,2) index by
varchar2(20);
3
aa_salary taa_salary;
4
v_name varchar2(20);
5
begin
6
aa_salary('yuechaotian') := 3300;
7
aa_salary('guoguo') := 3600;
8
aa_salary('oratea') := 3200;
9
aa_salary('hot_dog') := 3300;
10
v_name := aa_salary.first;
11
while v_name is not null loop
12
dbms_output.put_line( 'Name: ' || v_name || ' | Salary: ' ||
aa_salary(v_name) );
13
v_name := aa_salary.next(v_name);
14
end loop;
15
end;
16
/
Name: guoguo | Salary:
3600
Name: hot_dog | Salary:
3300
Name: oratea | Salary:
3200
Name: yuechaotian | Salary:
3300
PL/SQL 过程已成功完成。
SQL> |
以 binary_integer
作为索引的联合数组,是按照数字大小来排序的。而从上面的例子看,以 varchar2
作为索引的联合数组,似乎是按照ASCII码的顺序来排列的,当然这只是猜测,有待进一步验证。不过以 varchar2
作为索引的联合数组,很少会重视排序的情况。
2. 数值下标
大部分时候,我们还是使用数值作为联合数组的下标。使用时,注意一下各种下标的范围即可:
|
INDEX BY clause |
Minimum value |
Maximum value |
INDEX BY BINARY_INTEGER
|
-231 + 1 |
231 - 1 |
INDEX BY PLS_INTEGER
|
-231 + 1 |
231 - 1 |
INDEX BY NATURAL
|
0 |
231 - 1 |
INDEX BY POSITIVE
|
1 |
231 - 1 |
INDEX BY SIGNTYPE
|
-1 |
1 |
INDEX BY VARCHAR(N)
|
Any string within specified length |
Any string within specified
length |
我们还记得联合数组是
可以稀疏存储数据的吧?对于用习惯传统的数组的人,可能会觉得稀疏数组有点怪异,它有什么用呢?我举一个系统应用的例子:
做 PL/SQL 开发时,我们会发现自己一次又一次地编写并执行相同的查询
SQL,很多时候,这些数据都是“静态”的(数据不变)。如果在用户的一个会话期内这些数据没有变化,那么为什么我们不能在内存中把这些数据保存起来并直接使用呢?要知道,我们一次次地执行的这个
SQL,其执行计划已经在共享池中了,它所对应的数据也很可能已经在 SGA 的 buffer cache 中了。我们还用得着一次一次地软解析这个 SQL,再从
buffer cache 中获取数据,并返回到该会话的 PGA 中吗?
如果你的业务处理是这样的:在用户的一个会话期内,只从该“静态”表中获取一次数据,保存到该会话的 PGA
中,并在后继操作中多次使用该数据。那么你可以考虑使用集合。而 Oracle 所提供的这
三种集合方法里,联合数组是最适合了,因为它具有
稀疏的特性,我们可以灵活地使用这个功能。我们一步一步地说明什么时候适合使用稀疏的联合数组:
在一个 MIS 系统中,使用表 RYJBXX 保存人员的基本信息。在某个逻辑复杂的业务中,我需要对每一个人进行处理,处理时需要从表 RYJBXX
中获取每个人的基本信息。我可以每次获取一条记录,就像下面的伪码:
|
DECLARE rec_ryjbxx ryjbxx%ROWTYPE; ... BEGIN ...
FOR ... LOOP ...
SELECT * INTO rec_ryjbxx FROM ryjbxx WHERE rybh = v_rybh; ...
END LOOP; ... END; |
如果需要处理 3w 人,那么这个查询 SQL 就得执行 3w 次。虽然它用上了索引,这个 SQL 执行很快,但 3w
次执行的时间累加后就很可观了。这时候可以考虑使用集合实现。我们学过
批量绑定,很容易想到的就是采用这种方法,下面用联合数组重写这段伪码:
|
DECLARE TYPE taa_ryjbxx IS TABLE OF ryjbxx%ROWTYPE INDEX BY PLS_INTEGER; aa_ryjbxx taa_ryjbxx; ... BEGIN ...
SELECT * BULK COLLECT INTO aa_ryjbxx FROM ryjbxx;
FOR ... LOOP …
END LOOP; ... END; |
是的,它执行很快,似乎是最完美的解决方法了。那么,我们考虑一个问题:在后继的编码中,我如何使用联合数组 aa_ryjbxx
中的数据呢?比如我要使用编号为‘001’的人员的年龄,如何从 aa_ryjbxx
中获取?除了采用循环一个一个地比较,我没有更好的办法!看来批量绑定又增加了使用上的复杂度。
为什么不试试联合数组的稀疏特性呢?它可以稀疏地存储数据,而且断开的下标之间是不会浪费空间的。比如我可以使用个人编号作为下标,来实现这个功能:
|
DECLARE
TYPE taa_ryjbxx IS TABLE OF ryjbxx%ROWTYPE
INDEX BY PLS_INTEGER;
aa_ryjbxx
taa_ryjbxx;
...
BEGIN
...
FOR rec_ryjbxx IN ( SELECT * FROM ryjbxx )
LOOP
aa_ryjbxx( rec_ryjbxx.grbh
) := rec_ryjbxx;
END LOOP;
FOR ... LOOP
-- 可以用个人编号作为下标,使用集合的各种方法。比如
EXISTS
IF aa_ryjbxx.EXISTS( v_grbh )
THEN
...
END IF;
END LOOP;
...
END; |
那么在后继的编码中,我可以很方便地取出相应人员的基本信息了。不要忘记集合有很多的
方法可以使用。
关于使用集合缓存数据的方法,做一点总结:
* 这种方法是内存与磁盘IO之间的平衡。每个会话都有它自己的集合数据的拷贝,存放在各自的 PGA
中。如果有100个用户并发,那么将会有100份集合数据的拷贝,你得考虑机器的内存是否够用。
*
在以下情况中,可以考虑使用该技术:在多用户应用中的小的“静态”表;用户只访问一个大的“静态”表的一小部分数据;在批处理程序中处理大表(即只有1个会话占用大量内存的情况)。