记录和集合是Oracle提供的两种复合类型,复合类型是指包含其他类型的类型。PL/SQL的标量类型是一种不包含其他类型的变量,比如字符串、数字类型都是标量类型。复合类型是相对于标量类型而言的,复合类型的内部包含其他的标量类型,因此称为复合类型。
记录类型
记录只是一个用来组织其他标量类型的容器,本身是没有价值的,它里面的每一个变量拥有自己的值。为了定义记录类型,必须要先定义一个记录所包含的标量类型变量的类型,然后声明这种类型的变量,声明记录类型比标量类型多了一个步骤。PL/SQL的开发人员可以将记录看作是一种单行多列的数据类型,记录类型可以在PL/SQL块的声明区、子程序或包的声明部分进行定义,定义以TYPE关键字开头,示例如下:
DECLARE
-- 声明记录类型
TYPE emp_rec IS RECORD(
dept_row dept%ROWTYPE, -- 声明来自dept表行的嵌套记录
empno NUMBER NOT NULL DEFAULT 7369, -- 员工编号,默认值为7369
empname VARCHAR(12) := '张三', -- 员工名称,初始值为张三
hiredate DATE DEFAULT SYSDATE, -- 雇佣日期,默认值为当前日期
sal NUMBER(7, 2));
-- 声明记录类型的变量
emp_info emp_rec;
BEGIN
NULL;
END
> 注:记录类型也可以嵌套,比如可以在记录类型的成员中指定记录类型,还可以指定集合类型和对象类型。
-
赋值
在定义了记录类型后,必须声明一个该集合类型的变量,有了该集合类型的变量,就可以通过使用如下的语法来使用记录类型了:
记录类型.记录成员
简单赋值
在使用记录类型时,最常见的是为记录赋初值,与为普通变量赋初值的语法相似,示例如下:record_name.field_name := expression;
record_name是记录名,field_name 是记录成员字段, expression可以是任何常量、变量、记录、集合类型、表达式、函数调用等。
记录类型赋值
除了通过为单个记录逐个的赋值之外,还可以为整个记录进行一次性赋值,最常见的方式是将一个记录类型赋予另一个记录类型的值。示例如下:DECLARE -- 定义记录类型 TYPE emp_rec IS RECORD( empno NUMBER, empname VARCHAR(20)); -- 定义与emp_rec具有相同成员的记录类型 TYPE emp_rec_copy IS RECORD( empno NUMBER, empname VARCHAR(20)); -- 声明记录类型的变量 emp_info1 emp_rec; emp_info2 emp_rec; emp_info3 emp_rec_copy; BEGIN -- 为emp_info1记录赋值 emp_info1.empno := 10; emp_info1.empname := '笑笑'; emp_info2 := emp_info1; -- 将emp_info1记录变量直接赋给emp_info2 --emp_info3 := emp_info1; -- 此语句出现错误,不同记录类型的变量不能相互赋值 END
注:如果一个记录类型的变量赋给另一个记录类型,两个记录的类型必须完全一致。
如果要清空一个记录类型的变量,可以简单的为该变量赋一个空的或未初始化的记录类型,即可清空所有的记录成员值。
使用SELECT语句赋值
要使用SELECT语句为记录赋值,SELECT语句的选择列表必须要与记录类型的成员个数及类型相匹配,否则Oracle将抛出异常,示例如下:DECLARE -- 定义记录类型 TYPE emp_rec IS RECORD( empno NUMBER, empname VARCHAR(20), job VARCHAR(30)); -- 声明记录类型的变量 emp_info emp_rec; BEGIN -- 为记录类型赋值 SELECT empno, empname, job INTO emp_info FROM emp WHERE empno = 10; END
-
操纵记录类型
除了可以使用SELECT INTO语句从表中获取记录类型的值之外,还可以使用记录类型对数据表进行操作,比如可以在INSERT语句中使用记录类型直接插入一行数据;在UPDATE语句中使用记录更新数据。
在INSERT语句中使用记录类型
在INSERT语句中,既可以使用一个独立的用TYPE语句定义的记录类型变量,也可以使用由%ROWTYPE定义的记录变量来插入数据。使用这种方式插入数据时,列的个数、顺序及类型必须要与表中的个数、顺序与类型完全匹配。
在UPDATE语句中使用记录类型
与INSERT语句类似,也可以在UPDATE语句中直接应用记录类型的变量,记录中成员的个数必须要与SET子句后面列出的列的个数相等,数据类型必须相兼容。在UPDATE中使用记录类型时,使用关键字ROW来代表完整的一行数据,ROW关键字只能出现在SET字句的左边,同时不能在ROW关键字的右边使用子查询。
在RETURNING语句中使用记录类型
可以在DML语句中包含一个RETURNING字句,用来返回被UPDATE、DELETE或INSERT操作所影响到的行,通过RETURNING字句,可以将受影响的行保存到一个记录,或者是一个记录集合。
记录变量只在下面的几种情况下才允许使用:
在UPDATE语句中SET子句的右边可以使用记录变量。
在INSERT语句中VALUES子句的后面,可以使用记录插入数据,VALUES后面不需要使用括号。
在RETURNING语句中INTO子句的后面,可以将受影响的行插入到记录变量。
记录变量是不允许出现在SELECT列表、WHERE子句、GROUP BY子句或ORDER BY子句中的。
集合类型
集合是PL/SQL提供的用来同时处理多个数据的一种数据结构,如果说记录是一种单行多列的数据结构,那么集合就是一种单列多行的数据结构。
集合类似于高级语言中的列表或一维数组,主要用来存储具有相同类型的元素的有序集合,每一个元素都有唯一的下标来标识当前元素在集合中的位置。
集合是一个比较广义的概念,在PL/SQL中提供了如下三种类型的集合:
索引表:也称为关联数组,这种类型的集合可以通过数字或字符串作为下标来查找其中的元素,类似于其他语言中的哈希表,索引表是一种仅在PL/SQL中使用的数据结构。
嵌套表:使用有序数字作为嵌套表的下标,可以容纳任意个数的元素。嵌套表与索引表最大的区别在于可以定义嵌套表类型,把嵌套表存储到数据库中,并能通过SQL语句进行操作。
变长数组:在定义时保存固定数量的元素,但可以在运行时改变其容量。变长数组与嵌套表一样,使用有序数字作为下标,也可以保存到数据库中,但是不如嵌套表灵活。
注:在PL/SQL中,嵌套表和索引表统称为PL/SQL表。
定义索引表
索引表在语法上接近于Java或C语言中的数组,它由键值对组成。键是唯一用来标识索引表中元素的识别符,类似于数组中的下标,只不过索引表的下标既可以是整数又可以是字符串。
PS:元素非连续存储;元素个数不固定;下标可以为正数、负数或字符串;
索引表不能被存储在Oracle数据表中,仅用来在PL/SQL中处理程序的结构,它既与高级语言中的数组类似,又有不同点,下面是一些使用索引表需要了解的关键点:
索引表不需要进行初始化,没有构造语法,在为其赋值之前不需要分配初始化空间,因此不需要动态的扩展其容量。
索引表不仅可以使用数字作为索引下标,而且可以使用变长的字符串来索引其中的元素。
当使用数字类型作为索引下标时,索引键可以为正数、0或负数,并且数字可以不连续。
操纵索引表
索引表类型和相应的变量都定义好了之后,就可以通过变量(下标)来操纵索引表。由于索引表可以不连续,因此可以在索引表中任意的为某个索引位置进行赋值。示例如下:
DECLARE
-- 定义索引表类型
TYPE idx_table IS TABLE OF VARCHAR2(12) INDEX BY PLS_INTEGER;
-- 定义索引表变量
v_emp idx_table;
BEGIN
-- 随机的为索引表赋值
v_emp(1) := '史密斯';
v_emp(20) := '克拉克';
v_emp(40) := '史瑞克';
v_emp(-10) := '杰瑞';
IF v_emp.EXISTS(8) THEN
DBMS_OUTPUT.PUT_LINE(v_emp(8));
END IF;
END
注:索引表只是一种PL/SQL编程结构,不需要使用构造函数进行构造,构造一个索引表会触发Oracle异常。而对于嵌套表和变长数组来说,由于这两类集合属于对象类型,因此需要先进行构造才能使用。
使用字符串作为索引表的下标类型(类似于高级语言中的哈希表,通过键来寻找值),示例如下:
DECLARE
-- 定义以VARCHAR2作为索引键的索引表
TYPE idx_deptno_table IS TABLE OF NUMBER(2) INDEX BY VARCHAR2(20);
-- 声明记录类型的变量
v_deptno idx_deptno_table;
BEGIN
-- 为索引表赋值
v_deptno('财务部') := 10;
v_deptno('研究部') := 20;
v_deptno('销售部') := 30;
END
也可以在索引表的定义中使用%TYPE或%ROWTYPE。在为INDEX BY指定%TYPE时,所引用的类型必须匹配整型或变长的字符串,示例如下:
DECLARE
-- 定义记录类型的索引表,以dname作为索引键类型
-- dname是VARCHAR2(14)类型
TYPE idx_dept_table IS TABLE OF dept%ROWTYPE INDEX BY dept.dname%TYPE;
-- 声明记录类型的变量
v_dept idx_dept_table;
-- 定义一个游标,用来查询dept表
CURSOR dept_cur IS
SELECT * FROM dept;
BEGIN
-- 使用游标FOR循环打开游标,检索数据
FOR deptrow IN dept_cur LOOP
-- 为索引表中的元素赋值
v_dept(deptrow.dname) := deptrow;
-- 输出部门的LOC列信息
DBMS_OUTPUT.PUT_LINE(v_dept(deptrow.dname).loc);
END LOOP;
END
定义嵌套表
嵌套表是对索引表的扩展,与索引表最大的不同在于嵌套表可以存储到Oracle数据库表中,而索引表仅仅只是内存表。除此之外,使用嵌套表时必须使用其构造语法对嵌套表进行初始化。嵌套表没有INDEX BY字句,这是与索引表之间最明显的区别,因为嵌套表必须用有序的关键字创建,而且关键字不能为负数。
PS:元素非连续存储;元素个数不固定;下标必须为有序类型,不可以为负数,只能从1开始;
注:当使用嵌套表元素时,必须首先使用构造语法初始化嵌套表。
操纵嵌套表
由于在嵌套表使用之前必须进行构造,未构造的嵌套表被自动的赋初始值NULL,这与索引表不同,索引表只是一种程序上的结构,而嵌套表是一种对象的类型,尽管它们都是PL/SQL表类型,但是在结构上有明显的区别。示例如下:
DECLARE
TYPE emp_name_table IS TABLE OF VARCHAR2(20); -- 员工名称嵌套表
TYPE deptno_table IS TABLE OF NUMBER(2); -- 部门编号嵌套表
dept_info dept_table;
emp_name_info emp_name_table := emp_name_table('张三', '李四');
BEGIN
DBMS_OUTPUT.PUT_LINE('员工1' || emp_name_info(1)); --访问嵌套表元素
DBMS_OUTPUT.PUT_LINE('员工2' || emp_name_info(2));
IF dept_info IS NULL THEN -- 判断嵌套表是否被初始化
dept_info := dept_table();
END IF;
dept_info.EXTEND(5); -- 扩充元素的个数
FOR i IN 1 .. 5 LOOP -- 循环遍历嵌套表元素个数
dept_info(i) :=i*10;
END LOOP;
-- 显示部门个数
DBMS_OUTPUT.PUT_LINE('部门个数:'||dept_info.COUNT);
END
注:可以通过IS NULL来判断嵌套表是否已经被构造来使用嵌套表。
与索引表一样,嵌套表也可以是不连续的,可以通过DELETE来删除嵌套表中的元素。
数据库中的嵌套表
如果在PL/SQL中使用嵌套表,那么其功能与索引表比较相似,嵌套表的一个重要的特色是支持作为数据表列存储,因此可以将嵌套表存储在数据表中或者是从数据表中取出嵌套表,这是索引不具有的功能。在数据表中创建和使用嵌套表类型的示例如下:
-- 1.创建嵌套表类型
CREATE TYPE empname_type IS TABLE OF VARCHAR(20);
-- 2.创建数据表时指定嵌套表列,同时要使用STORE AS指定嵌套表的存储表
CREATE TABLE dept_nested(
deptno NUMBER(2), -- 部门编号
dname VARCHAR2(20), -- 部门名称
emplist empname_type -- 部门员工列表
)NESTED TABLE emplist STORE AS empname_table;
注:表中嵌套表列的内容是单独进行存放的,Oracle将嵌套表列的内容存储到创建表时指定的存储表中。数据库表中的列实际上是指向对存储表的一个引用,类似于一个REF变量。存储表里的内容是不能直接进行访问的,必须通过SQL语句来操纵存储表中的数据。
使用INSERT、UPDATE语句对嵌套表进行操作,示例如下:
DECLARE
emp_list empname_type := empname_type('张三', '李四','王五','赵六', '钱七');
BEGIN
-- 可以在INSERT语句中传入一个嵌套表实例
INSERT INTO dept_nested VALUES (10, '财务部', emp_list);
-- 也可以直接在INSERT语句中实例化嵌套表
INSERT INTO dept_nested
VALUES
(20, '理财部', empname_type('张杰', '陈乐', '李林', '马新'));
-- 从数据库表中查询出嵌套表实例
SELECT emplist INTO emp_list FROM dept_nested WHERE deptno = 10;
-- 对嵌套表进行更新,然后使用UPDATE语句将嵌套表实例更新回数据库
emp_list(1) := '牛牛';
emp_list(2) := '笑笑';
emp_list(3) := '乐乐';
emp_list(4) := '欢欢';
emp_list(5) := '梦梦';
-- 使用更改过的emp_list更新嵌套表列
UPDATE empname_type SET emplist = emp_list WHERE deptno = 10;
END
注:不能在WHERE字句中使用嵌套表,以及其他一些隐含需要比较的地方,比如ORDER BY、GROUP
BY、DISTINCT子句中都不能使用嵌套表。
要删除数据字典中的嵌套表类型,可以使用DROP TYPE语句,示例如下:
DROP TYPE epmname_type;
注:当使用DELETE语句删除某一行数据时,嵌套表数据也相应的被删除。
定义变长数组
变长数组,顾名思义,是指数组长度可变化的数组,变长数组与C或Java数组的数据类型非常相似。在数组大小方面,变长数组在声明时会具有一个上界值,元素插入到变长数组中时,以索引1开始,直到在变长数组中声明的最大长度,而且变长数组的元素在内存中是连续存储的,变长数组中的元素顺序相对较固定。
PS:元素连续存储;元素个数固定;下标必须为有序类型,不可以为负数,只能从1开始。
注:变长数组与嵌套表一样,也可以存储到数据库中。
定义一个名为projectlist的变长数组,该数组具有50个元素,且每个元素的类型是VARCHAR2(16),示例如下:
TYPE projectlist IS VARRAY(50) OF VARCHAR2(16);
操纵变长数组
变长数组与嵌套表一样,需要使用构造语法进行初始化,否则其值将为NULL。定义构造函数既可以在语句块的声明部分,也可以在语句块的执行体部分。在构造函数中创建的初始值将从1开始安排索引,如果访问索引之外的元素,Oracle将会抛出下标超出数量的错误。与嵌套表相同的是可以使用EXTEND来扩展元素范围,但是必须注意的是,EXTEND不能超过变长数组的最大长度。示例如下:
DECLARE
TYPE projectlist IS VARRAY(50) OF VARCHAR2(16); -- 定义项目列表变长数组
TYPE empno_type IS VARRAY(10) OF NUMBER(4); -- 定义员工编号变长数组
-- 声明变长数组类型的变量,并使用构造函数进行初始化
project_list projectlist := projectlist('网站', 'ERP', 'CRM', 'CMS')
empno_list empno_type; -- 声明变长数组类型的变量
BEGIN
DBMS_OUTPUT.PUT_LINE(project_list(3)); -- 输出第3个元素的值
project_list.EXTEND; -- 扩展到第5个元素
project_list(5) := 'WORKFLOW'; -- 为第5个元素赋值
empno_list := empno_type(7011,7012,7013,7014,NULL,NULL,NULL,NULL,NULL,NULL); -- 构造empno_list
empno_list(9) := 8011; -- 为第9个元素赋初值
DBMS_OUTPUT.PUT_LINE(empno_list(9)); -- 输出第9个元素的值
END;
数据库中的变长数组
和嵌套表一样,变长数组也可以作为数据库表的列数据被存储到数据库中。在数据表中创建和使用变长数组类型的示例如下:
-- 1.创建一个变长数组的类型empname_varray_type,用来存储员工信息
CREATE OR REPLACE TYPE empname_varray_type IS VARRAY(20) OF VARCHAR(20);
-- 2.创建部门数据表
CREATE TABLE dept_varray(
deptno NUMBER(2), -- 部门编号
dname VARCHAR2(20), -- 部门名称
emplist empname_varray_type -- 部门员工列表
);
在表中操纵变长数组数据列的示例如下:
DECLARE
-- 声明并初始化变长数组
emp_list empname_varray_type := empname_varray_type('张三', '李四', '王五', '赵六', '钱七');
BEGIN
-- 向表中插入变长数组数据
INSERT INTO dept_varray VALUES (10, '财务部', emp_list);
-- 也可以直接在INSERT语句中实例化嵌套表
INSERT INTO dept_varray
VALUES
(20, '理财部', empname_varray_type('张杰', '陈乐', '李林', '马新'));
-- 使用SELECT语句从表中取出变长数组数据
SELECT emplist INTO emp_list FROM dept_varray WHERE deptno = 10;
-- 更新变长数组数据的内容
emp_list(1) := '笑笑';
-- 使用UPDATE语句更新变长数组数据
UPDATE dept_varray SET emplist = emp_list WHERE deptno = 10;
-- 删除记录同时删除变长数组数据
DELETE FROM dept_varray WHERE deptno = 20;
END
选择集合类型
这三种集合类型各有其优点,应该如何选择集合类型要根据应用程序的需求和集合的特性来全面考虑。
嵌套表与索引表,统称为PL/SQL表,两者相似之处如下所示:
嵌套表自索引表扩展而来,因此嵌套表包含索引表的所有表属性。
嵌套表与索引表都是使用下标对集合中的元素进行访问。
嵌套表与索引表的数据类型具有相同的结构。
两者不同之处如下所示:
嵌套表可以存储到数据库中,而索引表不能,因此如果表类型需要保存到数据库中,应该考虑使用嵌套表。
嵌套表合法的下标范围是1214748361,下标不能为负数;而索引表可以为负下标,范围为-21474836472147483647。因此如果考虑带负数的下标,应该使用索引表。
索引表在每次调用语句块或在包初始化时在内存中自动构建,能够保存容量不固定的信息,因为它的长度是大小可变的,其值不能为NULL;而嵌套表是一种对象类型,如果不显式使用构造函数,则其值为NULL,可以使用IS NULL进行检查。
嵌套表可以使用其他的方法,比如EXTEND和TRIM等方法进行操作,而索引表不需要。
PL/SQL会自动在主机数组和索引表之间进行转换,而嵌套表不能在主机数组之间进行转换。
如果需要将集合类型保存到数据库中,可以在变长数组与嵌套表之间进行选择,这两者的相同之处如下所示:
变长数组与嵌套表都使用下标符号对单个元素进行访问,在使用前都必须使用构造函数进行初始化。
都可以存储在数据库表中,都可以应用集合方法。
两者不同之处如下所示:
变长数组一旦声明,元素数目就被固定,而嵌套表没有一个明确的大小上限。
当存储到数据库中时,变长数组保持了元素的排序和下标的值,而嵌套表则不同。
结论:如果是只需要在PL/SQL中使用的集合,且元素个数较少,则优先考虑索引表。而如果要存储到数据库中,则需要选择嵌套表。如果数据元素可以固定,则优先考虑使用变长数组。
使用集合方法
集合方法就是内置于集合中并且能够操作集合的函数或过程,可以通过如下的语法进行调用:
集合变量名 .集合方法名[(参数)];
注:集合方法只能在PL/SQL中使用,不能在SQL语句中使用。
在PL/SQL提供的集合方法中,EXISTS、COUNT、LIMIT、FIRST、LAST、PRIOR和NEXT是函数;EXTEND、TRIM和DELETE是过程。EXISTS、PRIOR、NEXT、TRIM、EXTEND和DELETE对应的参数是集合的下标索引,通常是整数,但对于关联数组来说也可能是字符串。
注:只有EXISTS能用于空集合,如果在空集合上调用其他方法,PL/SQL就会抛出异常COLLECTION_IS_NULL。
-
使用EXISTS方法
EXISTS方法用于判断集合中指定的元素是否存在,如果指定的元素存在,则返回TRUE,否则返回FALSE。使用这个方法主要用于在访问一个未分配值得下标元素时,避免Oracle弹出NO DATA FOUND这样得错误。 -
使用COUNT方法
COUNT方法能够返回集合中包含的元素个数,该函数在判断集合的当前元素个数时非常有用,因为集合的当前大小并不总是能够确定,特别是对于嵌套表和索引表这类大小不固定的集合。
对于变长数组来说,COUNT值与LAST方法值恒等,但对于嵌套表来说,正常情况下COUNT值会和LAST值相等。但是,当我们从嵌套表中间删除一个元素时,COUNT值就会比LAST值小。注:在计算元素的个数时,COUNT方法会跳过已被删除的元素。
当传递的下标越界时,EXISTS会返回false,而不会引发下标超出界限的异常。 -
使用LIMIT方法
LIMIT方法用于返回集合元素的最大个数,对于变长数组来说,因为其元素个数固定,可以返回变长数组所允许的最大元素个数。而对于嵌套表和索引表来说,由于其元素个数没有限制,因此调用该方法将总是返回NULL。
在PL/SQL编程过程中,LIMIT方法一般用在条件表达式中用来比较当前的最大值。 -
使用FIRST和LAST方法
FIRST和LAST方法分别返回集合中第一个和最后一个(即最小的和最大的)元素的索引数字,而不是该元素的值。如果集合为空,则FIRST和LAST将返回NULL。如果集合仅包含一个元素,那么FIRST和LAST将返回相同的数字。
对于索引表来说,如果是以VARCHAR2类型作为索引表的键,那么将会基于字符串中字符的二进制值来返回最高和最低的键值。 -
使用PRIOR和NEXT方法
PRIOR会返回集合中特定索引值参数的元素的前一个索引值,NEXT会返回集合中特定索引值参数所指向的元素的下一个索引值。如果特定的元素没有前一个或后一个值,那么PRIOR或NEXT就会返回NULL值。
PRIOR和NEXT通常用来使用循环遍历所有的元素值,这种遍历方法比通过固定的下标索引更加可靠,因为在循环过程中,有些元素可能被插入或删除。特别是索引表,因为它的下标索引可能是不连续的,有可能是(1,2,4,8,16)或(‘A’,’E’,’I’,’O’,’U’)这样的形式。 -
使用EXTEND方法
EXTEND方法主要用来为嵌套表和变长数组扩展元素,该方法不能用于索引表,从使用上来说主要有如下三种形式:
EXTEND: 在集合末端添加一个空元素。
EXTEND(n): 在集合末端添加n个空元素。
EXTEND(n,i): 把第i个元素赋值n份,并添加到集合的末端。
第一种和第二种形式用来为元素添加特定个数的空元素,第三种形式可以从指定的元素赋值n份,然后添加到集合的尾端。注:如果一个集合未使用构造语法进行初始化,是不能使用EXTEND进行扩展的。如果嵌套表或变长数组添加了NOT NULL约束,也不能使用EXTEND的前面两种形式。
-
使用TRIM方法
使用EXTEND方法可以向集合中追加元素,使用TRIM方法用来从嵌套表或变长数组的尾端删除元素。TRIM有如下两种操作方式:
TRIM: 从集合末端删除一个元素。
TRIM(n): 从集合末端删除n个元素。注:与EXTEND类似,TRIM也不会忽略被删除的元素。
-
使用DELETE方法
DELETE方法用于从索引表和嵌套表中删除一个或多个元素,DELETE方法有如下三种形式:
DELETE: 删除集合中所有元素。
DELETE(n): 从以数字做主键的关联数组或者嵌套表中删除第n个元素。如果关联数组有一个字符串键,对应该键值的元素就会被删除。如果n为空,DELETE(n)不会做任何事情。
DELETE(m,n): 从关联数组或嵌套表中,把索引表范围m到n的所有元素删除。如果m值大于n或m和n中有一个为空,那么DELETE(m,n)就不做任何事情。注:由于变长数组的元素个数固定,因此在变长数组上使用DELETE是非法的。
-
集合的异常处理
在使用集合进行数据处理时,需要了解这几种类型可能会产生的异常及如何对这些异常进行处理。例如,去访问一个未被初始化的嵌套表,或访问索引表中一个不存在的元素,下标超出变长数组的最大范围都会引发异常。下面是集合中常见的异常情况:
COLLECTION_IS_NULL: 调用一个空集合的方法。
NOT_DATA_FOUND: 下标索引指向一个被删除的元素或索引表中不存在的元素。
SUBSCRIPT_BEYOND_COUNT: 下标索引值超过集合中的元素个数。
SUBSCRIPT_OUTSIDE_LIMIT: 下标索引超过允许范围。
VALUE_ERROR: 下标索引值为空,或不能转换成正确的键类型。当键被定义在PLS_INTEGER的范围内,而下标索引值超过这个范围时,就可能抛出这个异常。 -
使用批量绑定
在编写PL/SQL代码时,PL/SQL引擎通常会与SQL引擎进行交互,比如将SQL语句送到SQL引擎,SQL引擎在执行了SQL语句后,会返回一些数据给PL/SQL引擎。
频繁的交互会大大降低效率。如果使用PL/SQL的批量绑定特性,将一次性向SQL引擎发送所有的SQL语句,会显著的提高执行的性能。
要使用批量绑定,可以使用FORALL语句,该语句将输入的集合送到SQL引擎之前,通知PL/SQL引擎将集合中的所有元素进行批量的绑定。注:FORALL语句并不是一个FOR循环,它仅包含了一个重复的步骤,用来通知PL/SQL引擎在将SQL语句发送给SQL引擎之前,将集合中的所有元素批量的绑定,以便一次性将多个绑定到SQL语句的变量一次性发送给SQL引擎。
示例如下:
DECLARE TYPE dept_type IS VARRAY(20) OF NUMBER; -- 定义嵌套表变量 depts dept_type := dept_type(10, 30, 70); -- 实例化嵌套表,分配3个元素 BEGIN FORALL i IN depts.FIRST .. depts.LAST -- 循环嵌套表元素 DELETE FROM emp WHERE deptno = depts(i); -- 向SQL引擎发送SQL命令执行SQL操作 FOR i IN 1 .. depts.COUNT LOOP DBMS_OUTPUT.PUT_LINE('部门编号' || depts(i) || '的删除操作受影响的行为:' || SQL%BULK_ROWCOUNT(i)); END LOOP; END
注:在操作SQL数据操纵语句时,SQL引擎会隐式的打开一个名为SQL的游标。这个游标的标量属性%FOUND、%ISOPEN、%NOTFOUND和%ROWCOUNT,能够提供最近一次执行的SQL数据操作语句信息。
由于批量绑定是一次性对多个SQL语句进行操作,因此要获取当前哪个SQL语句执行后受影响的行数信息,可以使用%BULK_ROWCOUNT,该变量接收一个集合元素的索引值。
-
使用BUKL COLLECT
FORALL关键字用来批量绑定多个集合的变量到SQL引擎,与之相反的是,BULK COLLECT关键字则可以批量的从SQL引擎中批量接收数据到一个集合,可以在SELECT-INTO、FETCH-INTO和RETURNING-INTO子句中使用BULK COLLECT。
SQL引擎能批量绑定出现在INTO列表后的所有集合,对应的字段可以保存为标量类型或复合类型的值,其中也包括对象类型。SQL引擎会初始化和扩展集合(但是,它不能把变长数组的长度扩大到超过变长数组的最大长度值),然后从索引1开始,连续的插入元素并覆盖先前已存在的元素。示例如下DECLARE TYPE numtab IS TABLE OF emp.empno%TYPE; -- 员工编号嵌套表 TYPE nametab IS TABLE OF emp.empname%TYPE; -- 员工名称嵌套表 nums numtab; -- 定义嵌套表变量,无需初始化 names nametab; BEGIN SELECT empno, empname BULK COLLECT INTO nums, names FROM emp; -- 从emp表中查出员工编号和名称,批量插入到集合 FOR i IN 1 .. nums.COUNT LOOP -- 循环显示集合内容 DBMS_OUTPUT.PUT('nums(' || i || ')=' || nums(i) || ' '); DBMS_OUTPUT.PUT_LINE('names(' || i || ')=' || names(i)); END LOOP; END
下面是使用BULK COLLECT的一些限制:
不能对使用字符串类型作为键的索引表使用BULK COLLECT子句。
只能在服务器端的程序中使用BULK COLLECT,如果在客户端使用,就会产生一个不支持这种特性的错误。
BULK COLLECT INTO的目标对象必须是集合类型。