PL/SQL 支持定义两种复合数据类型:集合类型和记录类型。
复合数据类型存储具有内部元素的值。您可以将整个复合变量作为参数传递给子程序,也可以单独访问复合变量的内部元素。内部元素可以是普通标量元素,也可以是另一个复合元素。您可以在任何可以使用标量和变量的地方使用复合数据类型的标量元素。也可以在使用相同类型的复合变量的地方使用复合数据类型的复合元素。
在集合类型中,内部元素始终具有相同的数据类型。您可以通过集合变量的唯一索引访问集合变量的每个元素,语法如下:variable_name(index)。要创建集合变量,需要先定义集合类型,然后创建该类型的变量,或者使用 %TYPE 创建。
在记录类型中,内部元素可以有不同的数据类型,称为字段。您可以通过字段名称访问记录变量的每个字段,语法如下:variable_name.field_name。要创建记录变量,需要先定义一个记录类型,然后创建该类型的变量,或者使用 %ROWTYPE 或 %TYPE。
您可以创建一个元素为记录类型的集合类型,或者一个包含集合类型字段的记录类型。
另请参阅
:ref:` BULK COLLECT 子句`
PL/SQL有三种集合类型:关联数组、可变数组和嵌套表。
下表总结了三种集合类型的差异。
集合类型 | 元素数量 | 密度 | 未初始化状态 | 何处定义 | 作为复合类型的元素 | |
---|---|---|---|---|---|---|
关联数组 | 不指定 | String or PLS_INTEGER | 不定 | 空 | PL/SQL块或包 | 否 |
可变数组 | 指定 | INTEGER | 密集 | NULL | PL/SQL块、包或模式 | 模式下定义可以 |
嵌套表 | 不指定 | INTEGER | 开始密集,可变为稀疏 | NULL | PL/SQL块、包或模式 | 模式下定义可以 |
其中,
元素数量
如果指定了元素数量,则改数量为集合中的最大元素数。如果未指定元素数,则集合中的最大元素数为索引类型的数量上限。
密度
密集集合的元素之间没有间隙,第一个和最后一个元素之间的每个元素都必须已定义并具有一个值(该值可以为 NULL,除非该元素具有 NOT NULL 约束)。稀疏集合的元素之间可以存在间隙。
未初始化状态
空集合,一个没有元素的集合。要添加元素,需要调用集合的 EXTEND 方法。
NULL集合,一个不存在的集合。要将 NULL 集合更改为存在的状态,必须通过将其设置为空或为其分配非空值来初始化它(通过调用相关构造函数)。无法通过直接调用 EXTEND 方法来初始化一个 NULL 集合。
何处定义
PL/SQL块中定义的集合类型属于本地类型。它仅支持在块中使用,并且仅当块位于独立子程序或包子程序中时才会被存储在数据库中。
包规范中定义的集合类型是公共类型。可以通过包名限定(package_name.type_name)从包外部引用它。它会一直存储在数据库中,直到删除包。
在模式中定义的集合类型是独立类型。您可以使用 “CREATE TYPE” 创建它。它存储在数据库中,直到您使用 “DROP TYPE” 删除它。
作为复合类型的元素
要成为复合类型的元素类型,集合类型必须是独立集合类型,即定义在模式中的集合类型。
关联数组是一组键值对。每个键都是一个唯一的索引,用于定位与之相关联的值,语法为:variable_name(index).
索引的数据类型可以是字符串类型(VARCHAR2、VARCHAR、STRING 或 LONG)或 PLS_INTEGER。其中数据是按索引排序顺序存储的,而不是按创建顺序存储的。
与数据库表一样,关联数组:
在填充之前为空(但不为NULL)
可以容纳不定量的元素,您可以在不知道其位置的情况下访问这些元素
与数据库表不同,关联数组:
不需要磁盘空间或网络操作
不能使用 DML 语句进行操作
关联数组不支持构造函数。
另请参阅
参考表PL/SQL集合类型,了解更多关联数组特性
示例 6-1以字符串为索引的关联数组
当前示例定义一种按字符串索引的关联数组,并声明该类型的变量,用三个元素填充关联数组变量,然后更改一个元素的值,并打印值(按字符串排序顺序,而不是按创建顺序)。
\set SQLTERM / DECLARE -- Associative array indexed by string: TYPE ass_type IS TABLE OF INT -- Associative array type INDEX BY VARCHAR2(64); -- indexed by string age ass_type; -- Associative array variable i VARCHAR2(64); -- Scalar variable BEGIN -- Add elements (key-value pairs) to associative array: age('zs') := 25; age('ls') := 30; age('ww') := 40; -- Change value associated with key 'Smallville': age('zs') := 26; -- Print associative array: i := age.FIRST; -- Get first element of array WHILE i IS NOT NULL LOOP RAISE NOTICE '% is % years old', i, age(i); i := age.NEXT(i); -- Get next element of array END LOOP; END; /
结果:
NOTICE: ls is 30 years old NOTICE: ww is 40 years old NOTICE: zs is 26 years old
示例 6-2函数返回以 PLS_INTEGER 为索引的关联数组
当前示例定义了一种以 PLS_INTEGER 为索引的关联数组,以及一个返回该关联数组类型的函数。
\set SQLTERM / DECLARE TYPE area_of_circle IS TABLE OF NUMBER INDEX BY PLS_INTEGER; area area_of_circle; --result variable num INT = 3; FUNCTION get_area_of_circle ( num INT ) RETURN area_of_circle IS s area_of_circle; BEGIN FOR i IN 1..num LOOP s(i) := 2 * 3.14 * i * i; END LOOP; RETURN s; END; BEGIN area= get_area_of_circle (num); for i in 1..num LOOP RAISE NOTICE 'area(%) = %', i, area(i); END LOOP; END; /
结果:
NOTICE: area(1) = 6.28 NOTICE: area(2) = 25.12 NOTICE: area(3) = 56.52
想要声明一个关联数字的常量,您必须创建一个函数为关联数组的元素赋值,然后在声明常量时调用该函数为常量进行初始化。
示例 6-3声明关联数组常量
当前示例中函数对关联数组的操作,与嵌套表和可变数组的构造函数一致
\set SQLTERM / CREATE OR REPLACE PACKAGE PKG1 AUTHID CURRENT_USER IS TYPE MY_ASS_TYPE IS TABLE OF VARCHAR2(20) INDEX BY PLS_INTEGER; FUNCTION Init_My_ASS_TYPE RETURN MY_ASS_TYPE; END PKG1; / \set SQLTERM / CREATE OR REPLACE PACKAGE BODY PKG1 IS FUNCTION Init_My_ASS_TYPE RETURN MY_ASS_TYPE AS VAL MY_ASS_TYPE; BEGIN VAL(-8) := '-eight'; VAL(0) := 'zero'; VAL(2) := 'two'; VAL(4) := 'four'; VAL(7) := 'seven'; RETURN VAL; END Init_My_ASS_TYPE; END PKG1; / \set SQLTERM / DECLARE val CONSTANT PKG1.MY_ASS_TYPE := PKG1.Init_My_ASS_TYPE(); index PLS_INTEGER := val.FIRST(); BEGIN WHILE index IS NOT NULL LOOP RAISE NOTICE '%, %',index, val(index); index := val.NEXT(index); END LOOP; END; /
结果:
NOTICE: -8, -eight NOTICE: 0, zero NOTICE: 2, two NOTICE: 4, four NOTICE: 7, seven
关联数组适用于:
查找一个相对较小的表,每次调用子程序或初始化声明子程序的包时,都可以在内存中构造它
向数据库服务器传递集合和从数据库服务器传递集合
关联数组用于临时数据存储。要使关联数组在数据库会话的生命周期内保持存在,请在包规范中声明它,并在包体中对其进行填充赋值。
可变数组是一个数组,其元素数为从零(空)到声明的最大值之间,大小不等。
要访问可变数组变量的元素,可以使用语法variable_name(index)。指数的下限为 1;上限是当前元素的数量。上限在添加或删除元素时会发生变化,但不能超过声明时指定的最大值。从数据库中存储和检索可变数组时,其索引和元素顺序保持对应。
未初始化的可变数组变量是一个空集合。必须通过构造函数或者为其赋予一个非空的值来初始化它。
另请参阅
参考表PL/SQL集合类型,以了解关联数组特性的概要
参考《数据库SQL语言参考》,以了解可变数组的更多相关信息
示例 6-4 可变数组
当前示例定义了一个本地可变数组类型,声明了该类型的变量并使用构造函数初始化它,定义了一个打印可变数组的存储过程。该示例调用该存储过程三次:分别为初始化变量后,更改两个元素的值后,以及使用构造函数更改所有元素的值后。
\set SQLTERM / DECLARE TYPE VARRAY_TYPE IS VARRAY(4) OF VARCHAR2(15); -- VARRAY type -- varray variable initialized with constructor: class VARRAY_TYPE := VARRAY_TYPE('zs', 'ls', 'ww', 'zl'); PROCEDURE show_class (heading VARCHAR2) IS BEGIN RAISE NOTICE '%', heading; FOR i IN 1..4 LOOP RAISE NOTICE '%.%' ,i ,class(i); END LOOP; RAISE NOTICE '---'; END; BEGIN show_class('2001 Class:'); class(3) := 'xx'; -- Change values of two elements class(4) := 'xm'; show_class('2005 Class:'); -- Invoke constructor to assign new values to varray variable: class := VARRAY_TYPE('xz', 'xw', 'xc', 'xx'); show_class('2009 Class:'); END; /
结果:
NOTICE: 2001 Class: NOTICE: 1.zs NOTICE: 2.ls NOTICE: 3.ww NOTICE: 4.zl NOTICE: --- NOTICE: 2005 Class: NOTICE: 1.zs NOTICE: 2.ls NOTICE: 3.xx NOTICE: 4.xm NOTICE: --- NOTICE: 2009 Class: NOTICE: 1.xz NOTICE: 2.xw NOTICE: 3.xc NOTICE: 4.xx NOTICE: ---
可变数组适用于以下情况:
已知元素的最大数量。
需要按顺序访问元素。
因为必须同时存储或检索所有元素,所以可变数组可能不适用于拥有大量元素的情况。
在数据库中,嵌套表是一种可以不指定顺序来存储未指定数量的行的类型。
从数据库中检索嵌套表值到PL/SQL嵌套表变量时,PL/SQL会从1开始为行提供连续索引。使用这些索引,可以访问嵌套表变量的各个行。语法是 variable_name(index)。从数据库中存储和检索嵌套表时,嵌套表的索引和行顺序可能会不稳定。
当添加或删除元素时,嵌套表变量占用的内存量可以动态的增加或减少。
未初始化的嵌套表变量是一个 NULL 集合。必须通过构造函数或为其赋予非空值来初始化它。
另请参阅
参考表PL/SQL集合类型,以了解关联数组特性的概要
参考《数据库SQL语言参考》,以了解数组相关的更多信息
示例 6-5 块内部的嵌套表类型
当前示例定义一个块内部的嵌套表类型,然后声明该类型的变量(使用构造函数初始化),并定义一个打印嵌套表的存储过程。
该示例调用该存储过程三次:初始化变量后、更改一个元素的值后,以及使用构造函数更改所有元素的值后。在第二次构造函数调用之后,嵌套表只有两个元素。引用元素 3 会引发错误。
\set SQLTERM / DECLARE TYPE NESTTAB_TYPE IS TABLE OF VARCHAR2(15); -- nested table type -- nested table variable initialized with constructor: fruits NESTTAB_TYPE := NESTTAB_TYPE('Apple', 'Orange', 'Banana', 'PEAR'); PROCEDURE show_fruits (heading VARCHAR2) IS BEGIN RAISE NOTICE '%', heading; FOR i IN fruits.FIRST .. fruits.LAST LOOP -- For first to last element RAISE NOTICE '%', fruits(i); END LOOP; RAISE NOTICE '---'; END; BEGIN show_fruits('Initial Values:'); fruits(3) := 'Watermelon'; -- Change value of one element show_fruits('Current Values:'); fruits := NESTTAB_TYPE('Strawberry', 'Pineapple'); -- Change entire table show_fruits('Current Values:'); END; /
结果:
NOTICE: Initial Values: NOTICE: Apple NOTICE: Orange NOTICE: Banana NOTICE: PEAR NOTICE: --- NOTICE: Current Values: NOTICE: Apple NOTICE: Orange NOTICE: Watermelon NOTICE: PEAR NOTICE: --- NOTICE: Current Values: NOTICE: Strawberry NOTICE: Pineapple NOTICE: ---
示例 6-6独立的嵌套表类型
当前示例定义了一个独立的类型 nest_type,以及一个独立的存储过程 show_nesttype 来打印该类型的变量。
匿名块声明一个类型为 nest_type 的嵌套表变量,用构造函数将其初始化为空,并在初始化变量后和使用构造函数更改所有元素的值后调用两次 show_nesttype。
\set SQLTERM / CREATE OR REPLACE TYPE nest_type IS TABLE OF NUMBER; / \set SQLTERM / CREATE OR REPLACE PROCEDURE show_nesttype (nt nest_type) AUTHID DEFINER IS i NUMBER; BEGIN i := nt.FIRST; IF i IS NULL THEN RAISE NOTICE 'nest type is empty'; ELSE WHILE i IS NOT NULL LOOP RAISE NOTICE 'nt(%) = %', i, nt(i); i := nt.NEXT(i); END LOOP; END IF; RAISE NOTICE '---'; END show_nesttype; / \set SQLTERM / DECLARE nt nest_type := nest_type(); -- nested table variable initialized to empty BEGIN show_nesttype(nt); nt := nest_type(1,3,99,1001); show_nesttype(nt); END; /
结果:
NOTICE: nest type is empty NOTICE: --- NOTICE: nt(1) = 1 NOTICE: nt(2) = 3 NOTICE: nt(3) = 99 NOTICE: nt(4) = 1001 NOTICE: ---
从概念上讲,嵌套表类似于具有任意数量元素的一维数组。但是,嵌套表与数组的不同之处在于:
数组声明时需指定元素数,但嵌套表没有。嵌套表的大小可以动态增加。
数组总是密集的。嵌套数组最初是密集的,但它会因为元素的删除而变得稀疏。
嵌套表适用于以下情况:
未指定元素的数量。
索引值是不连续的。
需要删除或更新某些元素,但不能同时删除或更新所有元素。
集合的构造函数(constructor)是一个系统定义的函数,与集合类型同名,返回值为对应集合类型。
构造函数调用的语法是:
collection_type ( [ value [, value ]... ] )
如果参数列表为空,则构造函数返回一个空集合。否则,构造函数将返回包含指定值的集合。
可以在变量声明和块的执行部分中将返回的集合分配给相同类型的集合变量。
示例 6-7将可变数组变量初始化为空
这个例子调用了一个构造函数两次:将可变数组变量 class 在其声明中初始化为空,并在块的执行部分为其提供新值。存储过程 show_class 打印变量 class 元素值。为了确定集合何时为空,show_class 使用了集合的 COUNT 方法。
\set SQLTERM / DECLARE TYPE VARRAY_TYPE IS VARRAY(4) OF VARCHAR2(15); class VARRAY_TYPE := VARRAY_TYPE(); -- initialize to empty PROCEDURE show_class (heading VARCHAR2) IS BEGIN RAISE NOTICE '%', heading; IF class.COUNT = 0 THEN RAISE NOTICE 'Empty'; ELSE FOR i IN 1..4 LOOP RAISE NOTICE '%.%' ,i ,class(i); END LOOP; END IF; RAISE NOTICE '---'; END; BEGIN show_class('Class:'); class := VARRAY_TYPE('xx', 'xm', 'xz', 'xh'); show_class('Class:'); END; /
结果:
NOTICE: Class: NOTICE: Empty NOTICE: --- NOTICE: Class: NOTICE: 1.xx NOTICE: 2.xm NOTICE: 3.xz NOTICE: 4.xh NOTICE: ---
可以通过以下方式为集合变量赋值:
调用构造函数来创建集合,并将其分配给集合变量。
使用赋值语句将另一个现有集合变量的值赋值给它。
将其作为 OUT 或 IN OUT 参数传递给子程序,然后在子程序内赋值。
要为集合变量的标量元素赋值,使用 collection_variable_name(index) 语法引用这些元素,并为其赋值。
另请参阅
只有当集合变量具有相同的数据类型时,才能将集合分配给集合变量。只有元素类型相同是无法互相赋值的。
示例 6-8数据类型兼容的集合赋值
当前示例中,可变数组类型 VARRAY_TYPE1 和 VARRAY_TYPE2 有相同的元素类型 TEXT。集合变量 varray1 和 varray2 有相同的数据类型 VARRAY_TYPE1, 但是集合变量 varray3 是数据类型 VARRAY_TYPE2。varray1 给 varray2 赋值成功,但是 varray1 给 varray3 赋值失败。
\set SQLTERM / DECLARE TYPE VARRAY_TYPE1 IS VARRAY(5) OF TEXT; TYPE VARRAY_TYPE2 IS VARRAY(5) OF TEXT; varray1 VARRAY_TYPE1 := VARRAY_TYPE1('Jones', 'Wong', 'Marceau'); varray2 VARRAY_TYPE1; varray3 VARRAY_TYPE2; BEGIN varray2 := varray1; -- ok varray3 := varray1; -- error END; /
结果:
ERROR: can not cast from Collection type to Collection type. CONTEXT: PL/SQL function inline_code_block line 10 at assignment
您可以给可变数组或嵌套表变量赋 NULL 值或相同数据类型的 NULL 集合。任一赋值都会使变量为空。
以下示例中,先将嵌套表变量 city_names 初始化为非空值;然后为其分配空集合,使其为空;最后将其重新初始化为不同的非空值。
示例 6-9给嵌套表变量赋值NULL
\set SQLTERM / DECLARE TYPE cnames_tab IS TABLE OF VARCHAR2(30); city_names cnames_tab := cnames_tab( 'Beijing','Shanghai','Guangzhou','Shenzhen'); -- Initialized to non-null value empty_set cnames_tab; -- Not initialized, therefore null PROCEDURE show_city_names_status IS BEGIN IF city_names IS NULL THEN RAISE NOTICE 'city_names is null.'; ELSE RAISE NOTICE 'city_names is not null.'; END IF; END show_city_names_status; BEGIN show_city_names_status; city_names := empty_set; -- Assign null collection to city_names. show_city_names_status; city_names := cnames_tab ( 'Shipping','Sales','Finance','Payroll'); -- Re-initialize city_names show_city_names_status; END; /
结果:
NOTICE: city_names is not null. NOTICE: city_names is null. NOTICE: city_names is not null.
集合只有一个维度,但可以通过使集合的元素为另一个集合来构造一个多维集合。
示例 6-10二维可变数组(数组的数组)
当前示例中 nva 是一个二维可变数组(元素为整型可变数组的数组)。
\set SQLTERM / DECLARE TYPE v1 IS VARRAY(10) OF INTEGER; -- varray of integer va v1 := v1(2,3,5); TYPE v2 IS VARRAY(10) OF v1; -- varray of varray of integer nva v2 := v2(va, v1(1,2,3), v1(4,5), va); i INTEGER; va1 v1; BEGIN i := nva(2)(3); RAISE NOTICE 'i = %', i; nva.EXTEND; nva(5) := v1(6, 7); -- replace inner varray elements nva(4) := v1(8,9,10,10000); -- replace an inner integer element nva(4)(4) := 1; -- replace 43345 with 1 nva(4).EXTEND; -- add element to 4th varray element nva(4)(5) := 98; -- store integer 89 there END; /
结果:
NOTICE: i = 3
示例 6-11嵌套表类型的嵌套表及整型可变数组的嵌套表
当前示例中,ntb1 是元素为字符串嵌套表的嵌套表,ntb2 是元素为整型可变数组的嵌套表。
\set SQLTERM / DECLARE TYPE tab1 IS TABLE OF VARCHAR2(20); -- nested table of strings vtb1 tab1 := tab1('t1', 't2'); TYPE ntab1 IS TABLE OF tab1; -- nested table of nested tables of strings vntb1 ntab1 := ntab1(vtb1); TYPE tv1 IS VARRAY(10) OF INTEGER; -- varray of integers TYPE ntb2 IS TABLE OF tv1; -- nested table of varrays of integers vntb2 ntb2 := ntb2(tv1(1,2), tv1(3,4,5)); BEGIN vntb1.EXTEND; vntb1(2) := vntb1(1); vntb1.DELETE(1); -- delete first element of vntb1 vntb1(2).DELETE(1); -- delete first string from second table in nested table END; /
示例 6-12关联数组的关联数组及字符串可变数组的关联数组
当前示例中,aa1 是元素为关联数组的关联数组,ntb2是元素为字符串可变数组的嵌套表。
\set SQLTERM / DECLARE TYPE tb1 IS TABLE OF INTEGER INDEX BY INT; -- associative arrays v4 tb1; v5 tb1; TYPE aa1 IS TABLE OF tb1 INDEX BY PLS_INTEGER; -- associative array of v2 aa1; -- associative arrays TYPE va1 IS VARRAY(10) OF VARCHAR2(20); -- varray of strings v1 va1 := va1('t1', 't2'); TYPE ntb2 IS TABLE OF va1 INDEX BY PLS_INTEGER; -- associative array of varrays v3 ntb2; BEGIN v4(1) := 1; -- populate associative array v4(2) := 23; v4(456) := 456; v2(23) := v4; -- populate associative array of associative arrays v3(34) := va1(11, 22, 33, 44); -- populate associative array varrays v2(35) := v5; -- assign empty associative array to v2(35) v2(35)(2) := 55; END; /
要确定一个集合变量与另一个集合变量的大小,必须定义在该内容中小于的含义,并编写一个返回 TRUE 或 FALSE 的函数。
不能将关联数组变量与 NULL 值或其它值相互比较。
除了比较嵌套表的相等和不相等外,您无法使用其它关系运算符对两个集合变量进行比较。这种限制也适用于隐式比较。例如,集合变量不能出现在 DISTINCT、GROUP BY 或 ORDER BY 子句中。
与 NULL 值比较时,可以使用 IS [NOT] NULL 运算符。
您可以使用 IS [NOT] NULL 运算符,将可变数组和嵌套表变量与 NULL 进行比较,但是不能使用关系运算符等于(=)和不等于(<>,!=或^=)进行比较。
示例 6-13将可变数组和嵌套表变量与NULL进行比较
\set SQLTERM / DECLARE TYPE VARRAY_TYPE IS VARRAY(4) OF VARCHAR2(15); -- VARRAY type class VARRAY_TYPE; -- varray variable TYPE NEST_TYPE IS TABLE OF TEXT; -- nested table type fruits NEST_TYPE := NEST_TYPE('Pear', 'Apple'); -- nested table variable BEGIN IF class IS NULL THEN RAISE NOTICE 'class IS NULL'; ELSE RAISE NOTICE 'class IS NOT NULL'; END IF; IF fruits IS NOT NULL THEN RAISE NOTICE 'fruits IS NOT NULL'; ELSE RAISE NOTICE 'fruits IS NULL'; END IF; END; /
结果:
NOTICE: class IS NULL NOTICE: fruits IS NOT NULL
两个嵌套表变量只有在拥有相同的元素集的情况下才相等(任何顺序都可)。
如果两个嵌套表变量具有相同的嵌套表类型,并且该嵌套表类型没有记录类型的元素,则可以使用关系运算符比较这两个变量的相等(=)或不相等(<>,!=,^=)。
另请参阅
记录类型的比较
示例 6-14嵌套表的相等和不相等比较
当前示例用关系运算符来比较嵌套表变量的相等和不相等。
\set SQLTERM / DECLARE TYPE NEST_TYPE IS TABLE OF VARCHAR2(30); -- element type is not record type fruits1 NEST_TYPE := NEST_TYPE('Apple','Banana','Pear','Orange'); fruits2 NEST_TYPE := NEST_TYPE('Apple','Pear','Banana','Orange'); fruits3 NEST_TYPE := NEST_TYPE('Apple','Pineapple','Strawberry'); BEGIN IF fruits1 = fruits2 THEN RAISE NOTICE 'dept_names1 = dept_names2'; END IF; IF fruits2 != fruits3 THEN RAISE NOTICE 'dept_names2 != dept_names3'; END IF; END; /
结果:
NOTICE: dept_names1 = dept_names2 NOTICE: dept_names2 != dept_names3
集合方法是PL/SQL子程序,既可以是一个返回集合信息的函数,也可以是一个对集合进行操作的过程。集合方法使集合更易于使用,更易于维护。
下表总结了集合方法情况。
方法 | 类型 | 描述 |
---|---|---|
DELETE | 存储过程 | 从集合中删除元素 |
TRIM | 存储过程 | 从可变数组或嵌套表的末尾删除元素 |
EXTEND | 存储过程 | 向可变数组或嵌套表的末尾增加元素 |
FIRST | 函数 | 返回集合的第一个索引 |
LAST | 函数 | 返回集合的最后一个索引 |
COUNT | 函数 | 返回集合元素的数量 |
LIMIT | 函数 | 返回集合可以包含的最大的元素数量 |
PRIOR | 函数 | 返回指定索引之前的索引 |
NEXT | 函数 | 返回指定索引的下一个索引 |
集合方法的基本调用语法为:
collection_name.method
集合方法可以出现在任何可以调用PL/SQL子程序(函数或过程)的地方,SQL 语句中除外。
在子程序中,集合参数假定绑定到它的参数的属性。可以将集合方法应用于此类参数。对于varray数组参数,无论参数模式如何,LIMIT的值总是源于参数类型定义。
DELETE是从集合中删除元素的过程。
这个方法有以下几种形式:
DELETE从任何类型的集合中删除所有元素。
此操作会立即释放分配给已删除元素的内存。
在关联数组或嵌套表(但不是varray数组)中:
DELETE(n)删除索引为n的元素(如果该元素存在);如果元素不存在,什么都不做。
DELETE(m,n)删除索引在m到n范围内的所有元素,如果m和n都存在且m<=n;否则,什么都不做。
对于这两种DELETE形式,PL/SQL为删除的元素保留占位符。因此,已删除的元素仍包含在集合的内部空间中,您可以通过为其指定有效值来恢复已删除的元素。
示例 6-15嵌套表的DELETE方法
这个例子声明了一个嵌套表变量,用六个元素初始化它;删除并恢复第二个元素;删除一系列元素,然后恢复其中一个;然后删除所有元素。
恢复的元素与相应的已删除元素占用相同的内存。在初始化和每次删除操作后,程序show_nesttype会打印嵌套的表变量。
\set SQLTERM / DECLARE nt nest_type := nest_type(12, 34, 56, 78, 90); BEGIN show_nesttype(nt); nt.DELETE(1); -- Delete second element show_nesttype(nt); nt(1) := 1212; -- Restore second element show_nesttype(nt); nt.DELETE(1, 3); -- Delete range of elements show_nesttype(nt); nt(2) := 2222; -- Restore third element show_nesttype(nt); nt.DELETE; -- Delete all elements show_nesttype(nt); END; /
结果:
NOTICE: nt(1) = 12 NOTICE: nt(2) = 34 NOTICE: nt(3) = 56 NOTICE: nt(4) = 78 NOTICE: nt(5) = 90 NOTICE: --- NOTICE: nt(2) = 34 NOTICE: nt(3) = 56 NOTICE: nt(4) = 78 NOTICE: nt(5) = 90 NOTICE: --- NOTICE: nt(1) = 1212 NOTICE: nt(2) = 34 NOTICE: nt(3) = 56 NOTICE: nt(4) = 78 NOTICE: nt(5) = 90 NOTICE: --- NOTICE: nt(4) = 78 NOTICE: nt(5) = 90 NOTICE: --- NOTICE: nt(2) = 2222 NOTICE: nt(4) = 78 NOTICE: nt(5) = 90 NOTICE: --- NOTICE: nest type is empty NOTICE: ---
示例 6-16字符串索引的关联数组使用DELETE方法
此示例填充按字符串索引的关联数组,并删除所有元素,从而释放分配给它们的内存。接下来,该示例将替换已删除的元素,即添加与已删除元素具有相同索引的新元素。 新的替换元素不会与相应的已删除元素占用相同的内存。最后,该示例删除一个元素,然后删除一系列元素。程序show_str显示了操作的效果。
\set SQLTERM / DECLARE TYPE str_type IS TABLE OF INTEGER INDEX BY TEXT; str str_type; PROCEDURE show_str IS i TEXT; BEGIN i := str.FIRST; IF i IS NULL THEN DBMS_OUTPUT.PUT_LINE('str is empty'); ELSE WHILE i IS NOT NULL LOOP RAISE NOTICE 'str.(%) = %', i, str(i); i := str.NEXT(i); END LOOP; END IF; RAISE NOTICE '---'; END show_str; BEGIN str('A') := 1; str('Z') := 26; str('B') := 2; show_str; str.DELETE; -- Delete all elements show_str; str('N') := 14; str('Z') := 26; str('D') := 4; str('X') := 24; str('Y') := 25; str('O') := 15; str('P') := 16; str('Q') := 17; str('H') := 8; show_str; str.DELETE('D'); -- Delete one element show_str; str.DELETE('O','P'); -- Delete range of elements show_str; str.DELETE('J','K'); -- Does nothing show_str; END; /
结果:
NOTICE: str.(A) = 1 NOTICE: str.(B) = 2 NOTICE: str.(Z) = 26 NOTICE: --- NOTICE: --- NOTICE: str.(D) = 4 NOTICE: str.(H) = 8 NOTICE: str.(N) = 14 NOTICE: str.(O) = 15 NOTICE: str.(P) = 16 NOTICE: str.(Q) = 17 NOTICE: str.(X) = 24 NOTICE: str.(Y) = 25 NOTICE: str.(Z) = 26 NOTICE: --- NOTICE: str.(H) = 8 NOTICE: str.(N) = 14 NOTICE: str.(O) = 15 NOTICE: str.(P) = 16 NOTICE: str.(Q) = 17 NOTICE: str.(X) = 24 NOTICE: str.(Y) = 25 NOTICE: str.(Z) = 26 NOTICE: --- NOTICE: str.(H) = 8 NOTICE: str.(N) = 14 NOTICE: str.(Q) = 17 NOTICE: str.(X) = 24 NOTICE: str.(Y) = 25 NOTICE: str.(Z) = 26 NOTICE: --- NOTICE: str.(H) = 8 NOTICE: str.(N) = 14 NOTICE: str.(Q) = 17 NOTICE: str.(X) = 24 NOTICE: str.(Y) = 25 NOTICE: str.(Z) = 26 NOTICE: ---
TRIM是从varray数组或嵌套表的末端删除元素的过程。
这个方法有以下形式:
如果集合拥有至少一个元素,那么TRIM会从集合的末端移除一个元素;否则,抛出异常。
TRIM(n)会从集合的末端移除n个元素,需要集合至少有n个元素;否则,会抛出异常。
TRIM操作集合的内部空间。如果DELETE删除了一个元素,KES的TRIM方法认为该元素已经不存在,所以在DETELE后使用TRIM方法会产生异常。
被TRIM的元素不包括在集合的内部空间中,并且不能通过为其指定有效值来恢复被TRIM的元素。
示例 6-17嵌套表使用TRIM方法
这个例子声明了一个嵌套表的变量,用六个元素初始化它;TRIM最后一个元素;DELETE第四个元素;然后TRIM最后两个元素。程序show_nesttype打印在初始化后以及TRIM和DELETE操作后的嵌套表变量。
\set SQLTERM / DECLARE nt nest_type := nest_type(10, 20, 30, 40, 50, 60); BEGIN show_nesttype(nt); nt.TRIM; -- Trim last element show_nesttype(nt); nt.DELETE(4); -- Delete fourth element show_nesttype(nt); nt.TRIM(2); -- Trim last two elements show_nesttype(nt); END; /
结果:
NOTICE: nt(1) = 10 NOTICE: nt(2) = 20 NOTICE: nt(3) = 30 NOTICE: nt(4) = 40 NOTICE: nt(5) = 50 NOTICE: nt(6) = 60 NOTICE: --- NOTICE: nt(1) = 10 NOTICE: nt(2) = 20 NOTICE: nt(3) = 30 NOTICE: nt(4) = 40 NOTICE: nt(5) = 50 NOTICE: --- NOTICE: nt(1) = 10 NOTICE: nt(2) = 20 NOTICE: nt(3) = 30 NOTICE: nt(5) = 50 NOTICE: --- NOTICE: nt(1) = 10 NOTICE: nt(2) = 20 NOTICE: ---
EXTEND方法向varray数组和嵌套表末端增加元素。
集合可以是空的,但不可以是NULL。
EXTEND方法有以下形式:
EXTEND向集合追加一个null元素。
EXTEND(n)向集合追加n个null元素。
EXTEND(n,i)向集合追加n个第i个元素的复制值。
EXTEND对集合的内部空间进行操作。也就是说,如果DELETE删除了一个元素,但保留了一个占位符,那么EXTEND会认为该元素存在。
示例 6-18嵌套表的EXTEND方法
当前示例声明了一个嵌套表变量,用三个元素初始化;追加了第一个元素的两个复制值;DELETE了第五个(也是最后一个)元素;然后又追加了一个NULL元素。 因为EXTEND认为被DELETE的第五个元素内存空间仍然存在,追加的NULL元素就变为第六个元素。程序show_nesttype打印初始化后和EXTEND及DELETE操作后的嵌套表变量。
\set SQLTERM / DECLARE nt nest_type := nest_type(10, 20, 30); BEGIN show_nesttype(nt); nt.EXTEND(2,1); -- Append two copies of first element show_nesttype(nt); nt.DELETE(5); -- Delete fifth element show_nesttype(nt); nt.EXTEND; -- Append one null element show_nesttype(nt); END; /
结果:
NOTICE: nt(1) = 10 NOTICE: nt(2) = 20 NOTICE: nt(3) = 30 NOTICE: --- NOTICE: nt(1) = 10 NOTICE: nt(2) = 20 NOTICE: nt(3) = 30 NOTICE: nt(4) = 10 NOTICE: nt(5) = 10 NOTICE: --- NOTICE: nt(1) = 10 NOTICE: nt(2) = 20 NOTICE: nt(3) = 30 NOTICE: nt(4) = 10 NOTICE: --- NOTICE: nt(1) = 10 NOTICE: nt(2) = 20 NOTICE: nt(3) = 30 NOTICE: nt(4) = 10 NOTICE: nt(6) =NOTICE: ---
EXISTS方法是可以告知您varray数组和嵌套表中是否存在指定元素的一个函数。
如果集合存在第n个元素,那么EXISTS(n)返回TRUE,否则,返回FALSE。如果n在范围外,EXISTS返回FALSE,而不是抛出异常。
对于一个被DELETE的元素,EXISTS(n)返回FALSE,即便DELETE为该元素保存了占位符。
示例 6-19嵌套表使用EXISTS方法
当前示例中用四个元素初始化嵌套表,删除第二个元素,打印元素1到6的值或状态。
\set SQLTERM / DECLARE TYPE IntList IS TABLE OF INTEGER; a IntList := IntList(1,2,3,4); BEGIN a.DELETE(2); -- Delete second element FOR i IN 1..6 LOOP IF a.EXISTS(i) THEN RAISE NOTICE 'a(%) = %', i, a(i); ELSE RAISE NOTICE 'a(%) does not exist', i; END IF; END LOOP; END; /
结果:
NOTICE: a(1) = 1 NOTICE: a(2) does not exist NOTICE: a(3) = 3 NOTICE: a(4) = 4 NOTICE: a(5) does not exist NOTICE: a(6) does not exist
FIRST和LAST都是函数。
如果集合至少拥有一个元素,FIRST和LAST返回第一个和最后一个元素的索引值(忽略已删除的元素,即使DELETE保留了他们的占位符)。 如果集合只有一个元素,则FIRST和LAST返回相同的索引。如果集合为空,则FIRST和LAST返回NULL。
6.9.5.1. 用于关联数组的FIRST和LAST方法
对于按PLS_INTEGER索引的关联数组,第一个和最后一个元素分别是索引最小和最大的元素。对于按字符串索引的关联数组,第一个和最后一个元素分别是具有最低和最高键值的元素。键值是有排列顺序的。
示例 6-20按PLS_INTEGER索引的关联数组的FIRST和LAST值
当前示例展示了按PLS_INTEGER索引的关联数组的FIRST和LAST值,删除了第一个和最后一个元素,然后又一次展示FISRT和LAST的值。
\set SQLTERM / DECLARE TYPE int_type IS TABLE OF INTEGER INDEX BY PLS_INTEGER; val int_type; PROCEDURE show_first_and_last IS BEGIN RAISE NOTICE 'FIRST = %', val.FIRST; RAISE NOTICE 'LAST = %', val.LAST; END show_first_and_last; BEGIN val(1) := 3; val(2) := 6; val(3) := 9; val(4) := 12; RAISE NOTICE 'Before deletions:'; show_first_and_last; val.DELETE(1); val.DELETE(4); RAISE NOTICE 'After deletions:'; show_first_and_last; END; /
结果:
NOTICE: Before deletions: NOTICE: FIRST = 1 NOTICE: LAST = 4 NOTICE: After deletions: NOTICE: FIRST = 2 NOTICE: LAST = 3
示例 6-21按字符串索引的关联数组的FIRST和LAST值
当前示例展示了按字符串索引的关联数组的FIRST和LAST值,删除了第一个和最后一个元素,然后又一次展示FISRT和LAST的值。
\set SQLTERM / DECLARE TYPE str_type IS TABLE OF INTEGER INDEX BY TEXT; str str_type; PROCEDURE show_first_and_last IS BEGIN RAISE NOTICE 'FIRST = %', str.FIRST; RAISE NOTICE 'LAST = %', str.LAST; END show_first_and_last; BEGIN str('C') := 3; str('A') := 1; str('D') := 4; str('B') := 2; RAISE NOTICE 'Before deletions:'; show_first_and_last; str.DELETE('A'); str.DELETE('D'); RAISE NOTICE 'After deletions:'; show_first_and_last; END; /
结果:
NOTICE: Before deletions: NOTICE: FIRST = A NOTICE: LAST = D NOTICE: After deletions: NOTICE: FIRST = B NOTICE: LAST = C
6.9.5.2. 用于Varray数组的FIRST和LAST方法
对于一个不为空的varray数组,FIRST总是返回1。对于每一个varray数组,LAST总是等于COUNT。
示例 6-22在FOR LOOP中打印Varray数组的FIRST和LAST值
\set SQLTERM / DECLARE TYPE class_type IS VARRAY(4) OF TEXT; class class_type; PROCEDURE show_class (heading VARCHAR2) IS BEGIN RAISE NOTICE '%',heading; IF class IS NULL THEN RAISE NOTICE 'Does not exist'; ELSIF class.FIRST IS NULL THEN RAISE NOTICE 'Has no members'; ELSE FOR i IN class.FIRST..class.LAST LOOP RAISE NOTICE '%.%', i, class(i); END LOOP; END IF; RAISE NOTICE '---'; END; BEGIN show_class('class Status:'); class := class_type(); -- class is funded, but nobody is on it. show_class('class Status:'); class := class_type('xx', 'xm'); -- Put 2 members on class. show_class('Initial class:'); class := class_type('xx', 'xm', 'xh', 'xq'); -- Change class. show_class('New class:'); END; /
结果:
NOTICE: class Status: NOTICE: Does not exist NOTICE: --- NOTICE: class Status: NOTICE: Has no members NOTICE: --- NOTICE: Initial class: NOTICE: 1.xx NOTICE: 2.xm NOTICE: --- NOTICE: New class: NOTICE: 1.xx NOTICE: 2.xm NOTICE: 3.xh NOTICE: 4.xq NOTICE: ---
6.9.5.3. 用于嵌套表的FIRST和LAST方法
对于嵌套表,LAST等于COUNT,除非删除了中间的元素,如果删除了中间元素,那么LAST比COUNT大。
示例 6-23在FOR LOOP中打印Varray数组的FIRST和LAST值
当前示例使用FOR LOOP语句结合class.FIRST和class.LAST值打印嵌套表。因为嵌套表可以使稀疏的,FOR LOOP语句只有在class.EXISTS(i)值是TRUE时才打印class(i)。
\set SQLTERM / DECLARE TYPE class_type IS TABLE OF VARCHAR2(15); class class_type; PROCEDURE show_class (heading VARCHAR2) IS BEGIN RAISE NOTICE 'heading'; IF class IS NULL THEN RAISE NOTICE 'Does not exist'; ELSIF class.FIRST IS NULL THEN RAISE NOTICE 'Has no members'; ELSE FOR i IN class.FIRST..class.LAST LOOP IF class.EXISTS(i) THEN RAISE NOTICE '%. %',i ,class(i); ELSE RAISE NOTICE '%. (to be hired)',i; END IF; END LOOP; END IF; RAISE NOTICE '---'; END; BEGIN show_class('class Status:'); class := class_type(); -- class is funded, but nobody is on it. show_class('class Status:'); class := class_type('xx', 'xm', 'xh', 'xq'); -- Add members. show_class('Initial class:'); class.DELETE(2,3); -- Remove 2nd and 3rd members. show_class('Current class:'); END; /
结果:
NOTICE: heading NOTICE: Does not exist NOTICE: --- NOTICE: heading NOTICE: Has no members NOTICE: --- NOTICE: heading NOTICE: 1. xx NOTICE: 2. xm NOTICE: 3. xh NOTICE: 4. xq NOTICE: --- NOTICE: heading NOTICE: 1. xx NOTICE: 2. (to be hired) NOTICE: 3. (to be hired) NOTICE: 4. xq NOTICE: ---
COUNT是一个返回集合中元素数量的函数(忽略已删除的元素,即使删除保留了它们的占位符)。
6.9.6.1. 用于Varray数组的COUNT方法
对于varray数组,COUNT总是等于LAST。如果增加或减少varray数组的大小(通过EXTEND或TRIM方法),COUNT值会改变。
示例 6-24Varray数组的COUNT和LAST值
当前示例展示了varray数组的COUNT和LAST值,包括用四个元素初始化后, EXTEND(3)之后以及TRIM(5)之后。
\set SQLTERM / DECLARE TYPE IntList IS VARRAY(10) OF INTEGER; a IntList := IntList(10,20,30,40); PROCEDURE show_count_and_last IS BEGIN RAISE NOTICE 'a.COUNT = %, a.LAST = %', a.count, a.last; END show_count_and_last; BEGIN show_count_and_last; a.EXTEND(6); show_count_and_last; a.TRIM(4); show_count_and_last; END; /
结果:
NOTICE: a.COUNT = 4, a.LAST = 4 NOTICE: a.COUNT = 10, a.LAST = 10 NOTICE: a.COUNT = 6, a.LAST = 6
6.9.6.2. 用于嵌套表的COUNT方法
对于嵌套表,COUNT等于LAST,除非删除了中间的元素,如果删除了中间元素,那么COUNT比LAST小。
示例 6-25嵌套表的COUNT和LAST值
当前示例展示了嵌套表的COUNT和LAST值,包括用四个元素初始化后,删除第三个元素后,以及在末端增加两个NULL元素后。最后,当前示例打印1到8元素的状态。
\set SQLTERM / DECLARE TYPE IntList IS TABLE OF INTEGER; a IntList := IntList(10,20,30,40); PROCEDURE show_count_and_last IS BEGIN RAISE NOTICE 'a.COUNT = %, a.LAST = %', a.count, a.last; END show_count_and_last; BEGIN show_count_and_last; a.DELETE(4); -- Delete third element show_count_and_last; a.EXTEND(2); -- Add two null elements to end show_count_and_last; FOR i IN 1..8 LOOP IF a.EXISTS(i) THEN IF a(i) IS NOT NULL THEN RAISE NOTICE 'a(%) = %',i ,a(i); ELSE RAISE NOTICE 'a(%) = NULL',i; END IF; ELSE DBMS_OUTPUT.PUT_LINE('a(' || i || ') does not exist'); RAISE NOTICE 'a(%) does not exist',i; END IF; END LOOP; END; /
结果:
NOTICE: a.COUNT = 4, a.LAST = 4 NOTICE: a.COUNT = 3, a.LAST = 3 NOTICE: a.COUNT = 5, a.LAST = 6 NOTICE: a(1) = 10 NOTICE: a(2) = 20 NOTICE: a(3) = 30 NOTICE: a(4) does not exist NOTICE: a(5) = NULL NOTICE: a(6) = NULL NOTICE: a(7) does not exist NOTICE: a(8) does not exist
LIMIT是一个函数,返回集合可以拥有的最大元素数。如果集合没有最大元素数,
LIMIT返回NULL。只有varray具有最大大小。
示例 6-26不同集合类型下的LIMIT和COUNT值
当前示例打印LIMIT和COUNT值,包括有四个元素的关联数组,两个元素的varray数组和三个元素的嵌套表。
\set SQLTERM / DECLARE TYPE int_type IS TABLE OF INTEGER INDEX BY PLS_INTEGER; aa int_type; -- associative array TYPE va_type IS VARRAY(4) OF INTEGER; va va_type := va_type(1,3); -- varray TYPE nest_type IS TABLE OF INTEGER; nt nest_type := nest_type(2,4,6); -- nested table BEGIN aa(1):= 3; aa(2):= 6; aa(3):= 9; aa(4):= 12; RAISE NOTICE 'aa.COUNT = %', aa.COUNT; RAISE NOTICE 'aa.LIMIT = %', aa.LIMIT; RAISE NOTICE 'va.COUNT = %', va.COUNT; RAISE NOTICE 'va.LIMIT = %', va.LIMIT; RAISE NOTICE 'nt.COUNT = %', nt.COUNT; RAISE NOTICE 'nt.LIMIT = %', nt.LIMIT; END; /
结果:
NOTICE: aa.COUNT = 4 NOTICE: aa.LIMIT =NOTICE: va.COUNT = 2 NOTICE: va.LIMIT = 4 NOTICE: nt.COUNT = 3 NOTICE: nt.LIMIT =
PRIOR和NEXT是允许在集合中前后移动的函数(忽略已删除的元素,即使删除保留了它们的占位符)。这些方法对于遍历稀疏集合非常有用。
给定一个索引:
PRIOR返回集合的前一个现有元素(如果存在)的索引。否则,PRIOR返回NULL。
对于任一集合c,c.PRIOR(c.FIRST)返回NULL。
NEXT返回集合的后一个现有元素(如果存在)的索引。否则,NEXT返回NULL。
对于任一集合c,c.NEXT(c.LAST)返回NULL。
给定的索引不必存在。但是,如果集合c是varray,且索引超过c.LIMIT,则:
c.PRIOR(index)返回c.LAST。
c.NEXT(index)返回NULL。
示例:
\set SQLTERM / DECLARE TYPE Arr_Type IS VARRAY(10) OF NUMBER; vnum Arr_Type := Arr_Type(); BEGIN vnum.EXTEND(4); vnum (1) := 10; vnum (2) := 20; vnum (3) := 30; vnum (4) := 40; RAISE NOTICE '%', vnum.prior (3400); RAISE NOTICE '%', vnum.next (3400); RAISE NOTICE '%', vnum.next (2); RAISE NOTICE '%', vnum.prior (3); END; /
结果:
NOTICE: 4 NOTICE:NOTICE: 3 NOTICE: 2
对于字符串索引的关联数组,前后索引由排序的键值决定。
示例 6-27PRIOR和NEXT方法
当前示例用六个元素初始化嵌套表,删除第四个元素,并且展示元素1到7的PRIOR和NEXT值。元素5和7不存在。元素1存在,尽管值为NULL。
\set SQLTERM / DECLARE TYPE nest_type IS TABLE OF NUMBER; nt nest_type := nest_type(NULL, 2, 3, 4, 5, 6); BEGIN nt.DELETE(5); RAISE NOTICE 'nt(5) was deleted.'; FOR i IN 1..7 LOOP RAISE NOTICE 'nt.PRIOR(%) = %', i, nt.PRIOR(i); RAISE NOTICE 'nt.NEXT(%) = %', i, nt.NEXT(i); END LOOP; END; /
结果:
NOTICE: nt(5) was deleted. NOTICE: nt.PRIOR(1) =NOTICE: nt.NEXT(1) = 2 NOTICE: nt.PRIOR(2) = 1 NOTICE: nt.NEXT(2) = 3 NOTICE: nt.PRIOR(3) = 2 NOTICE: nt.NEXT(3) = 4 NOTICE: nt.PRIOR(4) = 3 NOTICE: nt.NEXT(4) = 6 NOTICE: nt.PRIOR(5) = 4 NOTICE: nt.NEXT(5) = 6 NOTICE: nt.PRIOR(6) = 4 NOTICE: nt.NEXT(6) = NOTICE: nt.PRIOR(7) = 6 NOTICE: nt.NEXT(7) =
示例 6-28打印稀疏嵌套表的元素
当前示例逐个打印稀疏嵌套表的元素,包括使用FIRST和NEXT方法,打印从第一个元素到最后一个元素,使用LAST和PRIOR方法,打印从最后一个元素到第一个元素。
\set SQLTERM / DECLARE TYPE NumList IS TABLE OF NUMBER; n NumList := NumList(1, NULL, 3, 4, NULL, NULL, 7, 8, NULL, 10); index INTEGER; BEGIN RAISE NOTICE 'First to last:'; index := n.FIRST; WHILE index IS NOT NULL LOOP RAISE NOTICE 'n(%) = %', index, n(index); index := n.NEXT(index); END LOOP; RAISE NOTICE '-------------'; RAISE NOTICE 'Last to first:'; index := n.LAST; WHILE index IS NOT NULL LOOP RAISE NOTICE 'n(%) = %', index, n(index); index := n.PRIOR(index); END LOOP; END; /
结果:
NOTICE: First to last: NOTICE: n(1) = 1 NOTICE: n(2) =NOTICE: n(3) = 3 NOTICE: n(4) = 4 NOTICE: n(5) = NOTICE: n(6) = NOTICE: n(7) = 7 NOTICE: n(8) = 8 NOTICE: n(9) = NOTICE: n(10) = 10 NOTICE: ------------- NOTICE: Last to first: NOTICE: n(10) = 10 NOTICE: n(9) = NOTICE: n(8) = 8 NOTICE: n(7) = 7 NOTICE: n(6) = NOTICE: n(5) = NOTICE: n(4) = 4 NOTICE: n(3) = 3 NOTICE: n(2) = NOTICE: n(1) = 1
包规范中定义的集合类型与本地集合类型兼容有:作为参数进行传递时。
示例 6-29定义相同的包和本地集合类型
当前示例中,包规范和匿名块都定义了集合类型NumList。包定义了一个过程print_numlist,这过程有一个NumList参数。 匿名块声明了pkg.NumList类型的变量n1和定义在匿名块中的NumList类型的变量n2.匿名块可以允许n1作为print_numlist参数,也允许n2作为其参数。
\set SQLTERM / CREATE OR REPLACE PACKAGE pkg AS TYPE NumList IS TABLE OF NUMBER; PROCEDURE show_numlist (nums NumList); END pkg; / \set SQLTERM / CREATE OR REPLACE PACKAGE BODY pkg AS PROCEDURE show_numlist (nums NumList) IS BEGIN FOR i IN nums.FIRST..nums.LAST LOOP RAISE NOTICE 'nums(%) = %',i ,nums(i); END LOOP; RAISE NOTICE '---'; END; END pkg; / \set SQLTERM / DECLARE TYPE NumList IS TABLE OF NUMBER; -- local type identical to package type n1 pkg.NumList := pkg.NumList(1,2); -- package type n2 NumList := NumList(3,4); -- local type BEGIN pkg.show_numlist(n1); -- succeeds pkg.show_numlist(n2); -- succeeds END; /
结果
NOTICE: nums(1) = 1 NOTICE: nums(2) = 2 NOTICE: --- NOTICE: nums(1) = 3 NOTICE: nums(2) = 4 NOTICE: ---
您可以通过以下方式创建记录类型的变量:
定义RECORD类型,然后声明此类型变量。
通过%ROWTYPE来声明一个记录类型变量,该变量可以表示数据库表或视图的完整行或部分行。
通过%TYPE声明一个与之前已经声明过得记录类型相同的记录类型的变量。
对于RECORD类型的 record 变量,每个字段的初始化值为NULL,除非在定义类型时制定了不同的初始化值。
对于%ROWTYPE和%TYPE声明的 record 变量,每个字段的初始值也是NULL。该 record 变量不会继承引用项的初始化值。
注意
KES 记录类型暂不支持限定表达式。
KES 记录类型初始化值形式为: record 变量 记录类型 := (字段1,字段2);
声明 record 常量时,可以初始化值。
示例 6-30声明 record 常量
当前示例显示了初始化 record 常量 r。
\set SQLTERM / DECLARE TYPE Rec IS RECORD (a NUMBER, b NUMBER); r Rec := (0,1); BEGIN raise notice 'r = %',r; raise notice 'r.a = %',r.a; raise notice 'r.b = %',r.b; END; /
结果:
NOTICE: r = (0,1) NOTICE: r.a = 0 NOTICE: r.b = 1
在PL/SQL块中定义的RECORD类型是本地类型。仅在块中有效。只有该块在单独的过包子程序中时才会存储在数据库中。
在包规范中定义的RECORD类型是公共项。可以通过包名限定的方式(package_name.type_name)在包外引用。它会一直存储在数据库中,直到通过DROP PACKAGE语句删除包。
不能在模式级创建RECORD类型,因此,RECORD类型不可以作为ADT属性数据类型。
为了定义记录类型,需要指定名称和定义字段。需指定字段名和字段类型来定义字段。字段默认值为NULL。可以给字段加NOT NULL约束,这种情况下必须指定一个非NULL的初始值。如果没有NOT NULL约束,这个非NULL初始值可选。
包规范中定义的记录类型与相同定义的本地记录类型在以下情况兼容:
当参数类型为本地类型,传参类型为包规范中定义的记录类型,此时兼容。
通过:=赋值时兼容。
当做返回值类型时兼容。
另请参阅
示例 6-31记录类型定义和变量声明
当前示例定义了名为StuRecTyp的记录类型,为每一个字段指定了初始值。然后声明了名为stu_rec的该记录类型的变量,并且打印每个字段。
\set SQLTERM / DECLARE TYPE StuRecTyp IS RECORD ( stu_id NUMBER(4) NOT NULL := 1, stu_name VARCHAR2(30) NOT NULL := 'xm', score NUMBER(5,2) = 88.88 ); stu_rec StuRecTyp; BEGIN RAISE NOTICE 'stu_id = %', stu_rec.stu_id; RAISE NOTICE 'stu_name = %', stu_rec.stu_name; RAISE NOTICE 'score = %', stu_rec.score; END; /
结果:
NOTICE: stu_id = 1 NOTICE: stu_name = xm NOTICE: score = 88.88
示例 6-32嵌套的记录类型
当前示例定义了两个记录类型:stu_info_rec和stu_rec。类型stu_rec有一个字段是stu_info_rec类型。
drop table if exists student; create table student(id int PRIMARY KEY, name text, score number); insert into student values (1, 'xx', 99); \set SQLTERM / DECLARE TYPE stu_info_rec IS RECORD ( id student.id%TYPE, name student.name%TYPE ); TYPE stu_rec IS RECORD ( stu_info stu_info_rec, -- nested record score student.score%TYPE ); student1 stu_rec; BEGIN student1.stu_info.id := 1; student1.stu_info.name := 'xx'; student1.score := 88; RAISE NOTICE '%, %, %', student1.stu_info.id, student1.stu_info.name,student1.score; END; /
结果:
NOTICE: 1, xx, 88
示例 6-33包含Varray数组的记录类型
当前示例定义了varray数组类型 stu_info_rec ,和记录类型 stu_rec 。类型 stu_rec 有一个字段是 stu_info_rec 类型。
\set SQLTERM / DECLARE TYPE stu_info_rec IS VARRAY(2) OF VARCHAR2(20); TYPE stu_rec IS RECORD ( stu_info stu_info_rec := stu_info_rec('1', 'xx'), -- varray field score student.score%TYPE ); student1 stu_rec; BEGIN student1.score := 88; RAISE NOTICE '%, %, %', student1.stu_info(1), student1.stu_info(2),student1.score; END; /
结果:
NOTICE: 1, xx, 88
示例 6-34相同定义的包和本地记录类型
当前示例中,包pkg和匿名块都定义了记录类型rec_type。包定义了一个过程show_rec_type,这过程有一个rec_type类型的参数。
匿名块通过包类型pkg.rec_type声明了变量r1,以及本地类型(rec_type)变量r2。匿名块可以通过r1作为show_rec_type参数,但不能通过r2作为show_rec_type参数。
\set SQLTERM / CREATE OR REPLACE PACKAGE pkg AS TYPE rec_type IS RECORD ( -- package RECORD type f1 INTEGER, f2 VARCHAR2(4) ); PROCEDURE show_rec_type (rec rec_type); END pkg; / \set SQLTERM / CREATE OR REPLACE PACKAGE BODY pkg AS PROCEDURE show_rec_type (rec rec_type) IS BEGIN RAISE NOTICE '%', rec; END; END pkg; / \set SQLTERM / DECLARE TYPE rec_type IS RECORD ( -- local RECORD type f1 INTEGER, f2 VARCHAR2(4) ); r1 pkg.rec_type; -- package type r2 rec_type; -- local type BEGIN r1.f1 := 1; r1.f2 := 'aaaa'; r2.f1 := 2; r2.f2 := 'bbbb'; pkg.show_rec_type(r1); -- succeeds pkg.show_rec_type(r2); -- fails END; /
结果:
NOTICE: (1,aaaa) ERROR: cannot cast type record to "pkg.rec_type" LINE 1: CALL pkg.show_rec_type(r2) ^ QUERY: CALL pkg.show_rec_type(r2) CONTEXT: PL/SQL function inline_code_block line 16 at CALL
%ROWTYPE属性允许您声明一个 record 变量,该变量表示数据库表或视图的完整行或部分行。
6.11.4.1. 声明始终代表整行的 record 变量
要声明始终代表数据库表或视图的整行的 record 变量,请使用以下语法:
variable_name table_or_view_name%ROWTYPE;
对于表或视图的每一列, record 变量都有一个具有相同名称和数据类型的字段。
另请参阅
:ref:` %ROWTYPE 属性`,以了解更多的 %ROWTYPE 信息
示例 6-35 %ROWTYPE变量表示完整的数据库表行
当前示例声明了一个 record 变量,该变量表示表student的一行,为其字段赋值,并打印。
\set SQLTERM / DECLARE stu_rec student%ROWTYPE; BEGIN -- Assign values to fields: stu_rec.id := 1; stu_rec.name := 'xx'; stu_rec.score := '99'; -- Print fields: RAISE NOTICE 'stu_rec.id = %, stu_rec.name = %, stu_rec.score = %', stu_rec.id, stu_rec.name, stu_rec.score; END; /
结果:
NOTICE: stu_rec.id = 1, stu_rec.name = xx, stu_rec.score = 99
示例 6-36 %ROWTYPE变量不会继承初始化值或约束条件
当前示例创建有两列的表,每一列都有初始化值和NOT NULL约束。然后声明表示这张表的一行的 record 变量,并且打印字段,显示没有继承初始化值或NOT NULL约束。
DROP TABLE if exists t1; CREATE TABLE t1 ( a1 INTEGER DEFAULT 0 NOT NULL, a2 INTEGER DEFAULT 1 NOT NULL ); \set SQLTERM / DECLARE t1_row t1%ROWTYPE; BEGIN RAISE NOTICE 't1_row.a1 = %', t1_row.a1; RAISE NOTICE 't1_row.a2 = %', t1_row.a2; END; /
结果:
NOTICE: t1_row.a1 =NOTICE: t1_row.a2 =
6.11.4.2. 声明表示部分行的 record 变量
声明可以表示数据库表或视图的部分行的 record 变量,使用以下语法:
variable_name cursor%ROWTYPE;
其中的游标与查询关联。对于查询选择的每一列, record 变量都会有相对应的类型兼容的字段。如果查询选择了表或视图的每一列,那么这变量表示完整行;否则,变量表示部分行。游标必须是显示游标或强游标变量。
另请参阅
示例 6-37%ROWTYPE变量表示数据库表的部分行
当前示例定义了一个显示游标,其查询选择了student表的id, name和score这几列。然后声明了游标所选列的一个 record 变量。该变量表示student表的部分行。
\set SQLTERM / DECLARE CURSOR c IS SELECT id, name, score FROM student; stu c%ROWTYPE; BEGIN stu.id := 1; stu.name := 'xx'; stu.score := 88; RAISE NOTICE 'stu.id = %, stu.name = %, stu.score = %', stu.id, stu.name, stu.score; END; /
结果:
NOTICE: stu.id = 1, stu.name = xx, stu.score = 88
示例 6-38%ROWTYPE变量表示连接行
当前示例定义了一个显式游标,其查询是 join 连接,然后声明了一个 record 变量,该 record 变量的字段与游标选择的每一列对应。
CREATE TABLE stu_info (id int, name text); CREATE TABLE stu_grade (id int, score number); insert into stu_info values (1, 'xx'); insert into stu_grade values (1, 88); \set SQLTERM / DECLARE CURSOR c2 IS SELECT info.id, name, score FROM stu_info info, stu_grade grade WHERE info.id = grade.id; join_rec c2%ROWTYPE; -- includes columns from two tables BEGIN NULL; END; /
6.11.4.3. %ROWTYPE属性和虚拟列
如果使用%ROWTYPE属性定义一个 record 变量,该变量表示具有虚拟列的表的整行,则无法将该 record 变量插入表中。相应的,您必须将单个的 record 变量字段插入表中,不包括虚拟列。
示例 6-39%ROWTYPE 插入表中(错误方式)
当前示例创建一个 record 变量,该变量表示具有虚拟列的表的整行,填充该 record 变量,并将该 record 变量插入表中,从而导致错误。
DROP TABLE if exists test1; CREATE TABLE test1 ( destination VARCHAR2(100), departure_time DATE, delay NUMBER(10), vir_col DATE GENERATED ALWAYS AS (departure_time + delay/24/60/60) ); \set SQLTERM / DECLARE t1_rec test1%ROWTYPE; BEGIN t1_rec.destination := 'A'; t1_rec.departure_time := SYSDATE; t1_rec.delay := 100; INSERT INTO test1 VALUES t1_rec; END; /
结果:
ERROR: cannot insert into column "vir_col" DETAIL: Column "vir_col" is a generated column. CONTEXT: SQL statement "INSERT INTO test1 VALUES t1_rec" PL/SQL function inline_code_block line 8 at SQL statement
示例 6-40 %ROWTYPE插入表中(正确方式)
将各个 record 的字段插入表中,不包括虚拟列。
\set SQLTERM / DECLARE t1_rec test1%rowtype; BEGIN t1_rec.destination := 'A'; t1_rec.departure_time := SYSDATE; t1_rec.delay := 100; INSERT INTO test1 (destination, departure_time, delay) VALUES (t1_rec.destination, t1_rec.departure_time, t1_rec.delay); end; /
6.11.4.4. %ROWTYPE属性和隐藏列
假设使用%ROWTYPE属性定义 record 变量,该变量表示一张表的整行,表中含有隐藏列,然后将隐藏列变得可见。
如果使用游标定义 record 变量,游标表示部分行,将隐藏列变得可见不会改变 record 变量的结构。
如果使用表的整行定义 record 变量,使用SELECT INTO语句给 record 变量赋值,那么将隐藏列变得可见也不会改变 record 变量的结构。
另请参阅
《数据库 SQL 语言参考》以了解更多关于隐藏列的信息
示例 6-41使隐藏列可见会影响%ROWTYPE
DROP TABLE IF EXISTS t; CREATE TABLE t (a INT, b INT, c INT INVISIBLE); INSERT INTO t (a, b, c) VALUES (1, 2, 3); \set SQLTERM / DECLARE t_rec t%ROWTYPE; -- t_rec has fields a and b, but not c BEGIN SELECT * INTO t_rec FROM t WHERE ROWNUM < 2; -- t_rec(a)=1, t_rec(b)=2 RAISE NOTICE 'c = %', t_rec.c; END; /
结果:
ERROR: record "t_rec" has no field "c" CONTEXT: compilation of PL/SQL function "inline_code_block" near line 4
将隐藏列变得可见: .. code:
ALTER TABLE t MODIFY (c VISIBLE);
结果:
ALTER TABLE
再次运行以上匿名块:
\set SQLTERM / DECLARE t_rec t%ROWTYPE; -- t_rec has fields a and b, but not c BEGIN SELECT * INTO t_rec FROM t WHERE ROWNUM < 2; -- t_rec(a)=1, t_rec(b)=2 RAISE NOTICE 'c = %', t_rec.c; END; /
结果:
NOTICE: c = 3
record 变量是指 record 变量或复合 record 变量。
对于任何 record 变量,可以分别为每个字段指定一个值。
在有些情况下,可以将一个 record 变量的值赋给另一个 record 变量。
如果 record 变量表示数据库表或视图的完整行或部分行,则可以将这些行赋值给 record 变量。
仅在以下情况下可以用一个 record 变量给另一个 record 变量赋值:
两个 record 变量有相同的RECORD类型。
目标变量用RECORD类型声明,源变量用%ROWTYPE声明,他们的字段在数量和顺序上都匹配,相应的字段具有相同的数据类型。
对于复合record变量赋值,复合变量的类型不需要匹配。
例 6-42相同RECORD类型下一个 record 变量给另一个赋值
当前示例中,name1和name2有相同的RECORD类型,所以可以将name1赋值给name2。
\set SQLTERM / DECLARE TYPE stu_rec IS RECORD ( id student.id%TYPE DEFAULT 1, name student.name%TYPE DEFAULT 'xx' ); stu1 stu_rec; stu2 stu_rec; BEGIN stu1.id := 1; stu1.name := 'xx'; RAISE NOTICE 'stu1 = %', stu1; stu2 := stu1; RAISE NOTICE 'stu2 = %', stu2; END; /
结果:
NOTICE: stu1 = (1,xx) NOTICE: stu2 = (1,xx)
示例 6-43 %ROWTYPE 给RECORD类型的 record 变量赋值
当前示例中,目标变量用RECORD类型声明,源变量用%ROWTYPE声明,他们的字段在数量和顺序上都匹配,相应的字段也有相同的数据类型。
\set SQLTERM / DECLARE TYPE stu_rec IS RECORD ( id student.id%TYPE DEFAULT 1, name student.name%TYPE DEFAULT 'xx' ); CURSOR c IS SELECT id, name FROM student; rec_val stu_rec; rowtype_val c%ROWTYPE; BEGIN rec_val.id := 1; rec_val.name := 'xx'; RAISE NOTICE 'rec_val = %', rec_val; rowtype_val := rec_val; RAISE NOTICE 'rowtype_val = %', rowtype_val; END; /
结果:
NOTICE: rec_val = (1,xx) NOTICE: rowtype_val = (1,xx)
示例 6-44嵌套的 record 给另一个相同RECORD类型的 record 赋值
当前示例将一个嵌套 record 的值赋给另一个嵌套 record 。嵌套的 record 有相同的 record 类型,但嵌套他们的 record 不具有相同的 record 类型。
\set SQLTERM / DECLARE TYPE stu_rec IS RECORD ( id student.id%TYPE, name student.name%TYPE ); TYPE grade_rec IS RECORD ( stu_info stu_rec, -- nested record score student.score%TYPE ); TYPE email_rec IS RECORD ( stu_info stu_rec, -- nested record email TEXT ); stu_grade grade_rec; stu_email email_rec; BEGIN stu_grade.stu_info.id := 1; stu_grade.stu_info.name := 'xx'; stu_grade.score := 88; stu_email.stu_info := stu_grade.stu_info; stu_email.email := ( stu_email.stu_info.name || '@' || 'example.com' ); RAISE NOTICE '%', stu_email.email; END; /
结果:
NOTICE: xx@example.com
如果 record 变量表示数据库表或视图的完整行或部分行,则可以将这些行赋值给 record 变量。
6.12.1.1. 使用SELECT INTO将一行赋值给 record 变量
简单SELECT INTO语句的语法是:
SELECT select_list INTO record_variable_name FROM table_or_view_name;
对于select_list中的每一列, record 变量必须有对应的、类型兼容的字段。select_list中的列在顺序上必须与 Record 变量的字段一致。
另请参阅
SELECT INTO语句
示例 6-45SELECT INTO语句给 record 变量赋值
当前示例中, record 变量rec1表示student表的部分行和列name和id。SELECT INTO语句从student表中选择 score 是99的行,并且将这些行中的列name和id的值赋值给 Record 变量rec1的相应字段。
\set SQLTERM / DECLARE TYPE RecordTyp IS RECORD ( id student.id%TYPE, name student.name%TYPE ); rec1 RecordTyp; BEGIN SELECT id, name INTO rec1 FROM student WHERE score = 99; RAISE NOTICE 'rec1.id = %, rec1.name = %',rec1.id ,rec1.name; END; /
结果:
NOTICE: rec1.id = 1, rec1.name = xx
6.12.1.2. 使用FETCH将一行赋值给 record 变量
简单FETCH语句的语法是:
FETCH cursor INTO record_variable_name;
游标关联一个查询。对于查询所选的每一列, record 变量必须有相应地、类型兼容的字段。游标必须是显式游标或强游标变量。
另请参阅
示例 6-46FETCH给函数返回的 record 赋值
当前示例中,RECORD类型RecordTyp的每一个变量都表示表student的部分行——列id和name。游标和函数返回RecordTyp类型的值。 在函数中,FETCH语句将列id和name的值相应地赋值给RecordTyp类型的本地变量
\set SQLTERM / DECLARE TYPE RecordTyp IS RECORD ( id student.id%TYPE, name student.name%TYPE ); rec1 RecordTyp; FUNCTION f1 RETURN RecordTyp AS CURSOR c is select id, name from student; rec1 RecordTyp; BEGIN OPEN c; FETCH c INTO rec1; close c; return rec1; END; BEGIN rec1 = f1(); RAISE NOTICE 'rec1.id = %, rec1.name = %',rec1.id ,rec1.name; END; /
结果:
NOTICE: rec1.id = 1, rec1.name = xx
6.12.1.3. 使用SQL语句将行返回到PL/SQL record 变量
SQL语句INSERT、UPDATE和DELETE都有可选子句RETURNING INTO,该子句可以将受影响的行返回至 record 变量中。
示例 6-47 UPDATE 语句赋值给 record 变量
当前示例中,UPDATE语句更新了一个员工的薪水,并返回该员工的名字和新的薪水到 record 变量中。
\set SQLTERM / DECLARE TYPE RecordTyp IS RECORD ( id student.id%TYPE, name student.name%TYPE, score student.score%TYPE ); rec1 RecordTyp; BEGIN UPDATE student SET score = score - 1 WHERE id = 1 RETURNING id, name, score INTO rec1; RAISE NOTICE 'rec1 = %', rec1; END; /
结果:
NOTICE: rec1 = (1,xx,98)
将NULL值赋给 record 变量,意味着对 record 变量的每个字段都赋值为NULL。
此赋值是递归的;也就是说,如果一个字段是 record ,那么它的字段也会被赋值为NULL。
示例 6-48NULL赋值给 record 变量
当前示例在为 record 变量赋值NULL之前和之后打印该变量的字段。
\set SQLTERM / DECLARE TYPE RecordTyp IS RECORD ( id student.id%TYPE, name student.name%TYPE, score student.score%TYPE ); rec1 RecordTyp; BEGIN rec1.id = 1; rec1.name = 'xx'; rec1.score = 99; RAISE NOTICE 'rec1 = %', rec1; rec1 := NULL; RAISE NOTICE 'rec1 = %', rec1; END; /
结果:
NOTICE: rec1 = (1,xx,99) NOTICE: rec1 = (,,)
record无法被验证是否为NULL,相等或不相等。
这些布尔表达式是非法的:
My_Record IS NULL
My_Record_1 = My_Record_2
My_Record_1 > My_Record_2
如果确实有比较 record 变量的需要,那么可以自己写函数来实现 record 的比较。
PL/SQL扩展了SQL INSERT语句,可以允许向表中插入 record 变量。 - record 必须表示表中的行。 - 为了高效的向一张表插入一系列 record ,可以考虑将INSERT语句放入FORALL语句中来实现。
示例 6-49 通过插入带有默认值的 Record 变量来初始化表
当前示例创建了表schedule,通过将 Record 变量带有默认值及将 Record 变量插入表来初始化这张表
DROP TABLE IF EXISTS schedule; CREATE TABLE schedule ( week NUMBER, Mon VARCHAR2(10), Tue VARCHAR2(10), Wed VARCHAR2(10), Thu VARCHAR2(10), Fri VARCHAR2(10), Sat VARCHAR2(10), Sun VARCHAR2(10) ); \set SQLTERM / DECLARE default_week schedule%ROWTYPE; BEGIN default_week.Mon := '0900-1800'; default_week.Tue := '0900-1800'; default_week.Wed := '0900-1800'; default_week.Thu := '0900-1800'; default_week.Fri := '0900-1800'; default_week.Sat := 'Day Off'; default_week.Sun := 'Day Off'; FOR i IN 1..4 LOOP default_week.week := i; INSERT INTO schedule VALUES default_week; END LOOP; END; / \set SQLTERM ; SELECT * FROM schedule;
结果:
week | mon | tue | wed | thu | fri | sat | sun ------+-----------+-----------+-----------+-----------+-----------+---------+--------- 1 | 0900-1800 | 0900-1800 | 0900-1800 | 0900-1800 | 0900-1800 | Day Off | Day Off 2 | 0900-1800 | 0900-1800 | 0900-1800 | 0900-1800 | 0900-1800 | Day Off | Day Off 3 | 0900-1800 | 0900-1800 | 0900-1800 | 0900-1800 | 0900-1800 | Day Off | Day Off 4 | 0900-1800 | 0900-1800 | 0900-1800 | 0900-1800 | 0900-1800 | Day Off | Day Off (4 rows)
PL/SQL扩展SQL UPDATE语句,以便支持通过 record 变量来更新一行或多行表中的行。
record 变量必须表示表中的行。
为了高效的通过一系列 record 变量来更新行,可以考虑将UPDATE语句放在FORALL语句中实现。
示例 6-50 通过 record 变量更新行
当前示例更新表schedule中的前两周数据,主要通过将新数据放在 record 变量中,并且将这些 record 变量更新到表的前两周行数据中来实现。
\set SQLTERM / DECLARE default_week schedule%ROWTYPE; BEGIN default_week.Mon := 'Day Off'; default_week.Tue := 'Day Off'; default_week.Wed := '0800-1700'; default_week.Thu := '0800-1700'; default_week.Fri := '0800-1700'; default_week.Sat := '0800-1700'; default_week.Sun := '0800-1700'; FOR i IN 1..2 LOOP default_week.week := i; UPDATE schedule SET ROW = default_week WHERE week = i; END LOOP; END; / \set SQLTERM ; SELECT * FROM schedule order by week;
结果:
week | mon | tue | wed | thu | fri | sat | sun ------+-----------+-----------+-----------+-----------+-----------+-----------+----------- 1 | Day Off | Day Off | 0800-1700 | 0800-1700 | 0800-1700 | 0800-1700 | 0800-1700 2 | Day Off | Day Off | 0800-1700 | 0800-1700 | 0800-1700 | 0800-1700 | 0800-1700 3 | 0900-1800 | 0900-1800 | 0900-1800 | 0900-1800 | 0900-1800 | Day Off | Day Off 4 | 0900-1800 | 0900-1800 | 0900-1800 | 0900-1800 | 0900-1800 | Day Off | Day Off (4 rows)
这些限制应用在 record 变量的插入和更新中:
record 变量仅在以下地方被允许:
在UPDATE语句的SET子句右侧
在INSERT语句的VALUES子句中
在RETURNING分支子句的INTO子句中
record 变量不允许出现在SELECT列表,WHERE子句,GROUP BY子句或ORDER BY子句中。
关键字ROW仅被允许出现在SET子句的左侧。而且不允许在子查询中使用ROW。
在UPDATE语句中,如果ROW关键字使用了,那么SET子句仅允许出现一处。
如果INERT语句的VALUES子句包含 record 变量,该子句中其他变量或值就不允许存在。
如果RETURNING子句的INTO子句包含 record 变量,该子句中其他变量或值就不允许存在。
以下不支持:
嵌套RECORD类型
返回RECORD类型的函数
使用EXECUTE IMMEDIATE语句的 record 变量插入和更新