Oracle PL/SQL 集合:Varray、嵌套表和索引表

什么是集合?

集合是指特定数据类型的有序元素组。它可以是简单数据类型的集合,也可以是复杂数据类型的集合(如用户定义类型或记录类型)。

在集合中,每个元素都由一个称为“下标”的术语标识。集合中的每个项都分配有一个唯一的下标。可以通过引用该唯一下标来操作或获取集合中的数据。

当需要处理或操作大量相同类型的数据时,集合是最有用的。可以使用Oracle中的“BULK”选项将集合整体进行填充和操作。

集合根据结构、下标和存储进行分类,如下所示。

  • 索引表(也称为关联数组)
  • 嵌套表
  • Varrays

在任何时候,集合中的数据都可以通过三个术语来引用:集合名称、下标、字段/列名,格式为“<collection_name>(<subscript>).<column_name>”。在下面的部分中,您将进一步了解上述集合类别。

Varrays

Varray是集合方法的一种,其中数组的大小是固定的。数组的大小不能超过其固定值。Varray的下标是数值。以下是Varrays的属性。

  • 上限大小固定
  • 从下标‘1’开始顺序填充
  • 此集合类型始终是连续的,即我们不能删除任何数组元素。Varray可以整体删除,也可以从末尾修剪。
  • 由于其本质上始终是连续的,因此灵活性非常小。
  • 当数组大小已知且对所有数组元素执行类似活动时,使用它更合适。
  • 下标和序列始终保持稳定,即集合的下标和计数始终相同。
  • 它们需要在程序中使用之前进行初始化。对未初始化的集合执行的任何操作(EXISTS操作除外)都会引发错误。
  • 它可以创建为数据库对象,该对象在整个数据库中可见,也可以在子程序内部创建,只能在该子程序中使用。

下图将以图解方式解释Varray(连续)的内存分配。

下标 1 2 3 4 5 6 7
Xyz Dfv Sde Cxs Vbc Nhu Qwe

VARRAY语法

TYPE <type_name> IS VARRAY (<SIZE>) OF <DATA_TYPE>;
  • 在上面的语法中,type_name被声明为给定大小限制的‘DATA_TYPE’类型的VARRAY。数据类型可以是简单类型或复杂类型。

嵌套表

嵌套表是数组大小不固定的集合。它具有数字下标类型。以下是关于嵌套表类型的更多描述。

  • 嵌套表没有上限大小。
  • 由于上限大小不是固定的,因此每次使用集合之前,都需要扩展内存。我们可以使用‘EXTEND’关键字来扩展集合。
  • 从下标‘1’开始顺序填充。
  • 此集合类型可以是连续的,也可以是稀疏的,即我们可以创建连续的集合,也可以随机删除单个数组元素,这使其成为稀疏的。
  • 它提供了更大的灵活性,可以删除数组元素。
  • 它存储在系统生成的数据库表中,并可以在select查询中使用以获取值。
  • 下标和序列不稳定,即数组元素的下标和计数可能会有所不同。
  • 它们需要在程序中使用之前进行初始化。对未初始化的集合执行的任何操作(EXISTS操作除外)都会引发错误。
  • 它可以创建为数据库对象,该对象在整个数据库中可见,也可以在子程序内部创建,只能在该子程序中使用。

下图将以图解方式解释嵌套表(连续和稀疏)的内存分配。黑色的元素空间表示集合中的空元素,即稀疏。

下标 1 2 3 4 5 6 7
值(连续) Xyz Dfv Sde Cxs Vbc Nhu Qwe
值(稀疏) Qwe Asd Afg Asd Wer

嵌套表语法

TYPE <tvpe name> IS TABLE OF <DATA TYPE>;
  • 在上面的语法中,type_name被声明为‘DATA_TYPE’类型的嵌套表集合。数据类型可以是简单类型或复杂类型。

索引表

索引表是数组大小不固定的集合。与其它集合类型不同,在索引表中,下标可以由用户定义。以下是索引表的属性。

  • 下标可以是整数或字符串。在创建集合时,应指定下标类型。
  • 这些集合不是顺序存储的。
  • 它们本质上总是稀疏的。
  • 数组大小不是固定的。
  • 它们不能存储在数据库列中。它们应在会话中创建并在任何程序中使用。
  • 它们在维护下标方面提供了更大的灵活性。
  • 下标也可以是负数下标序列。
  • 它们更适合用于相对较小的值集合,其中集合可以在同一子程序中初始化和使用。
  • 它们在使用前无需初始化。
  • 它不能创建为数据库对象。它只能在子程序内部创建,并且只能在该子程序中使用。
  • 在此集合类型中不能使用BULK COLLECT,因为需要为集合中的每个记录显式指定下标。

下图将以图解方式解释索引表(稀疏)的内存分配。黑色的元素空间表示集合中的空元素,即稀疏。

下标(varchar) FIRST SECOND THIRD FOURTH FIFTH SIXTH SEVENTH
值(稀疏) Qwe Asd Afg Asd Wer

索引表语法

TYPE <type_name> IS TABLE OF <DATA_TYPE> INDEX BY VARCHAR2 (10);
  • 在上面的语法中,type_name被声明为‘DATA_TYPE’类型的索引表集合。数据类型可以是简单类型或复杂类型。子脚本/索引变量为VARCHAR2类型,最大尺寸为10。

集合中的构造函数和初始化概念

构造函数是Oracle提供的内置函数,其名称与对象或集合相同。在会话中首次引用对象或集合时,它们会首先执行。以下是集合上下文中构造函数的关键详细信息:

  • 对于集合,这些构造函数必须显式调用才能进行初始化。
  • Varray和嵌套表都需要在程序中引用之前通过这些构造函数进行初始化。
  • 构造函数会隐式扩展集合的内存分配(Varray除外),因此构造函数也可以为集合分配变量。
  • 通过构造函数为集合赋值永远不会使集合变得稀疏。

集合方法

Oracle提供了许多用于操作和处理集合的函数。这些函数在程序中对于确定和修改集合的各种属性非常有用。下表将列出不同的函数及其描述。

方法 描述 语法
EXISTS (n) 此方法返回布尔结果。如果第n个元素存在于该集合中,它将返回‘TRUE’,否则返回FALSE。未初始化的集合只能使用EXISTS函数。 <collection_name>.EXISTS(element_position)
COUNT 给出集合中元素的总数。 <collection_name>.COUNT
LIMIT 它返回集合的最大大小。对于Varray,它将返回已定义的固定大小。对于嵌套表和索引表,它返回NULL。 <collection_name>.LIMIT
FIRST 返回集合的第一个索引变量(下标)的值。 <collection_name>.FIRST
LAST 返回集合的最后一个索引变量(下标)的值。 <collection_name>.LAST
PRIOR (n) 返回集合中第n个元素的先前索引变量。如果没有先前索引值,则返回NULL。 <collection_name>.PRIOR(n)
NEXT (n) 返回集合中第n个元素的后续索引变量。如果没有后续索引值,则返回NULL。 <collection_name>.NEXT(n)
EXTEND 在集合末尾扩展一个元素。 <collection_name>.EXTEND
EXTEND (n) 在集合末尾扩展n个元素。 <collection_name>.EXTEND(n)
EXTEND (n,i) 在集合末尾扩展第i个元素的n个副本。 <collection_name>.EXTEND(n,i)
TRIM 从集合末尾删除一个元素。 <collection_name>.TRIM
TRIM (n) 从集合末尾删除n个元素。 <collection_name>.TRIM (n)
DELETE 删除集合中的所有元素。使集合变为空。 <collection_name>.DELETE
DELETE (n) 从集合中删除第n个元素。如果第n个元素为NULL,则此操作无效。 <collection_name>.DELETE(n)
DELETE (m,n) 删除集合中从第m个到第n个范围内的元素。 <collection_name>.DELETE(m,n)

示例1:子程序级别的记录类型

在本例中,我们将使用‘BULK COLLECT’来填充集合,并学习如何引用集合数据。

Record Type at Subprogram level

DECLARE
TYPE emp_det IS RECORD
(
EMP_NO NUMBER,
EMP_NAME VARCHAR2(150),
MANAGER NUMBER,
SALARY NUMBER
);
TYPE emp_det_tbl IS TABLE OF emp_det; guru99_emp_rec emp_det_tbl:= emp_det_tbl(); 
BEGIN
INSERT INTO emp (emp_no,emp_name, salary, manager) VALUES (1000,’AAA’,25000,1000);
INSERT INTO emp (emp_no,emp_name, salary, manager) VALUES (1001,'XXX’,10000,1000);
INSERT INTO emp (emp_no, emp_name, salary, manager) VALUES (1002,'YYY',15000,1000);
INSERT INTO emp (emp_no,emp_name,salary, manager) VALUES (1003,’ZZZ’,'7500,1000);
COMMIT:
SELECT emp no,emp_name,manager,salary BULK COLLECT INTO guru99_emp_rec
FROM emp;
dbms_output.put_line (‘Employee Detail');
FOR i IN guru99_emp_rec.FIRST..guru99_emp_rec.LAST
LOOP
dbms_output.put_line (‘Employee Number: '||guru99_emp_rec(i).emp_no); 
dbms_output.put_line (‘Employee Name: '||guru99_emp_rec(i).emp_name); 
dbms_output.put_line (‘Employee Salary:'|| guru99_emp_rec(i).salary); 
dbms_output.put_line(‘Employee Manager Number:'||guru99_emp_rec(i).manager);
dbms_output.put_line('--------------------------------');
END LOOP;
END;
/

代码解释

  • 代码行2-8:记录类型‘emp_det’被声明,包含emp_no、emp_name、salary和manager列,数据类型分别为NUMBER、VARCHAR2、NUMBER、NUMBER。
  • 代码行9:创建记录类型元素‘emp_det’的集合‘emp_det_tbl’。
  • 代码行10:声明变量‘guru99_emp_rec’为‘emp_det_tbl’类型,并使用null构造函数进行初始化。
  • 代码行12-15:将示例数据插入‘emp’表。
  • 代码行16:提交插入事务。
  • 代码行17:从‘emp’表获取记录,并使用“BULK COLLECT”命令将集合变量作为批量进行填充。此时,变量‘guru99_emp_rec’包含‘emp’表中存在的所有记录。
  • 代码行19-26:设置‘FOR’循环以逐个打印集合中的所有记录。集合方法FIRST和LAST用作循环的下限和上限。循环

输出:如上截图所示,当执行上述代码时,您将获得以下输出。

Employee Detail
Employee Number: 1000
Employee Name: AAA
Employee Salary: 25000
Employee Manager Number: 1000
----------------------------------------------
Employee Number: 1001
Employee Name: XXX
Employee Salary: 10000
Employee Manager Number: 1000
----------------------------------------------
Employee Number: 1002
Employee Name: YYY
Employee Salary: 15000
Employee Manager Number: 1000
----------------------------------------------
Employee Number: 1003
Employee Name: ZZZ
Employee Salary: 7500
Employee Manager Number: 1000
----------------------------------------------