Oracle_PL/SQL(7) 集合

pl/sql集合
处理单行单列数据,可以使用标量变量;
处理单行多列的数据,可以使用pl/sql记录(%rowtype,record);
处理单列多行数据,可以使用pl/sql集合。

pl/sql集合类型是类似于高级语言数组的一种复合数据类型。
包括:嵌套表(table),数组(varray)两种。

一、嵌套表
1.定义:
嵌套表用于处理相同类型的多个数值,其中每个数值称为一个元素。
元素的个数从1开始,元素个数的多少没有限制
1.2 语法:
type type_name is table of element_type;
identifier type_name;
说明:
type_name 是用户定义类型名
element_type 是元素类型
identifier 是用户使用自定义类型定义的变量
1.3 举例:
第一个例子:1个元素的例子
declare
type type_varchar2 is table of varchar2(20);
v_type type_varchar2;
begin
v_type:=type_varchar2('A');
dbms_output.put_line('输出元素:'||v_type(1));
end;
多个元素的例子
declare
type type_varchar2 is table of varchar2(20);
v_type type_varchar2;
begin
v_type:=type_varchar2('A','adsvad','svadfvadv');
dbms_output.put_line('输出元素:'||v_type(2));
end;
总结:
给嵌套表变量赋值时必须在嵌套表类型内添加元素,元素可以是1个或者多个。
不能将元素直接赋值给嵌套表变量。
嵌套表变量只有在赋值后才能被使用,通过下标来使用嵌套表变量内的元素。
下标是元素添加到嵌套表变量内的顺序。

二、数组(varray)
1.定义
数组用于处理相同类型的多个数值,其中每个数值称为一个元素。
元素的个数从1开始,元素的最大个数是有限制的。
2.语法:
type type_name is varray(size_limite) of element_type [not null];
identifier type_name;
说明:
type_name 是用户定义类型名
varray() 是数组的关键字
size_limite 是数组的长度
element_type 是元素类型
identifier 是用户使用自定义类型定义的变量
数组和嵌套表的区别:
关键字不同:table、varray
长度限制不同:table没有长度限制、varray有长度限制
3.举例
declare
type type_arr_varchar2 is varray(10) of varchar2(20);
v_arr type_arr_varchar2;
begin
v_arr:=type_arr_varchar2('张三','李四');
dbms_output.put_line('输出元素:'||v_arr(1));
dbms_output.put_line('输出元素:'||v_arr(2));
end;
用查询赋值:
declare
type type_arr_ename is varray(20) of emp.ename%type;
v_arr type_arr_ename:=type_arr_ename('');
begin
select ename into v_arr(1) from emp where empno=&no;
dbms_output.put_line('雇员名:'||v_arr(1));
end;
说明:
table和varray的功能是相同的,都可以用来存储相同类型的多个数值;
table和varray的使用方法是相同的,都需要先定义类型,再定义变量,
再将元素添加的变量中,最后通过下标来使用元素。
table和varray的属性是相同的。

三、集合属性
集合属性是数据库提供给用户的操作集合的方法(函数和过程),
过程包括:extend,trim,delete
函数包括:exists,count,limit,first,last,prior,next。
集合方法只能在pl/sql语句中使用,不能在sql语句中使用。
1.extend:
用于扩展集合变量的尺寸,并为它们增加元素。
集合变量的初始化:
通过集合类型构造函数可以给集合变量的初始化(给集合变量分配了大块的内存空间)。
extend是在给集合变量分配的大块内存空间内给元素分配小块的内存空间。
三种调用格式:
extend 分配一个元素的内存空间
extend(n) 分配n个元素的内存空间
extend(n,i) 分配n个元素的内存空间,并且用第i个元素给这n个元素赋初始值

第一个例子可以改写为下面的通用方式。
declare
type type_varchar2 is table of varchar2(20);
v_type type_varchar2:=type_varchar2();
begin
v_type.extend;
v_type(1):='A';
dbms_output.put_line('输出元素:'||v_type(1));
end;

table语法改良:
type type_name is table of element_type;
identifier type_name:=type_name();
varray语法改良:
type type_name is varray(size_limite) of element_type [not null];
identifier type_name:=type_name();

例1:extend用于table
declare
type type_tab_ename is table of emp.ename%type;
v_arr type_tab_ename:=type_tab_ename();
v_cnt number:=1;
begin
for rs in (select ename from emp) loop
v_arr.extend;
v_arr(v_cnt):=rs.ename;
dbms_output.put_line('雇员名:'||v_arr(v_cnt));
v_cnt:=v_cnt+1;
end loop;
end;

例2:extend(n)用于varray
select count(1) from emp;
declare
type type_arr_ename is varray(16) of emp.ename%type;
v_arr type_arr_ename:=type_arr_ename();
v_cnt number:=1;
begin
v_arr.extend(16);
for rs in (select ename from emp) loop
v_arr(v_cnt):=rs.ename;
dbms_output.put_line('雇员名:'||v_arr(v_cnt));
v_cnt:=v_cnt+1;
end loop;
end;
例3:extend(n,i)用于varray,且i元素必须已经存在
declare
type ename_table_type is varray(20) of varchar2(10);
ename_table ename_table_type;
begin
ename_table:=ename_table_type('mary');
ename_table.extend(5,1);
dbms_output.put_line('元素个数:'||ename_table.count);
for i in 1..ename_table.count loop
dbms_output.put_line('元素'||i||':'||ename_table(i));
end loop;
end;

2.trim:用于从集合尾部删除元素,
有两种调用格式:
trim :删除最后一个元素
trim(n):删除最后n个元素
适用于嵌套表和varray
declare
type ename_table_type is table of varchar2(10);
ename_table ename_table_type;
begin
ename_table:=ename_table_type('a','b','c','d','e');
ename_table.trim(2);
dbms_output.put_line('元素个数:'||ename_table.count);
end;

3.delete:删除指定元素,
有三种调用方式:
delete 删除全部元素
delete(n) 删除第n个元素(删除1个元素)
delete(m,n) 删除集合变量从m到n之间的所有元素
只适用于嵌套表,delete(n)delete(m,n)不适用于varray。
例1:delete 删除第n个元素
declare
type type_tab_ename is table of emp.ename%type;
v_arr type_tab_ename:=type_tab_ename();
v_cnt number:=1;
begin
for rs in (select ename from emp) loop
v_arr.extend;
v_arr(v_cnt):=rs.ename;
dbms_output.put_line('雇员名:'||v_arr(v_cnt));
v_cnt:=v_cnt+1;
end loop;
dbms_output.put_line('元素个数:'||v_arr.count);
v_arr.delete;
dbms_output.put_line('元素个数:'||v_arr.count);
end;
例1-2:varray
declare
type type_tab_ename is varray(16) of emp.ename%type;
v_arr type_tab_ename:=type_tab_ename();
v_cnt number:=1;
begin
for rs in (select ename from emp) loop
v_arr.extend;
v_arr(v_cnt):=rs.ename;
dbms_output.put_line('雇员名:'||v_arr(v_cnt));
v_cnt:=v_cnt+1;
end loop;
dbms_output.put_line('元素个数:'||v_arr.count);
v_arr.delete;
dbms_output.put_line('元素个数:'||v_arr.count);
end;
例2:delete(n) 删除第n个元素
declare
type type_tab_ename is table of emp.ename%type;
v_arr type_tab_ename:=type_tab_ename();
v_cnt number:=1;
begin
for rs in (select ename from emp) loop
v_arr.extend;
v_arr(v_cnt):=rs.ename;
dbms_output.put_line('雇员名:'||v_arr(v_cnt));
v_cnt:=v_cnt+1;
end loop;
dbms_output.put_line('元素个数:'||v_arr.count);
v_cnt:=v_arr.count;
v_arr.delete(5);
dbms_output.put_line('元素个数:'||v_arr.count);
for i in 1..v_cnt loop
if i=5 then
continue;
end if;
dbms_output.put_line('元素'||i||':'||v_arr(i));
end loop;
end;

例3:delete(m,n) 删除集合变量从m到n之间的所有元素
declare
type type_tab_ename is table of emp.ename%type;
v_arr type_tab_ename:=type_tab_ename();
v_cnt number:=1;
begin
for rs in (select ename from emp) loop
v_arr.extend;
v_arr(v_cnt):=rs.ename;
dbms_output.put_line('雇员名:'||v_arr(v_cnt));
v_cnt:=v_cnt+1;
end loop;
dbms_output.put_line('元素个数:'||v_arr.count);
v_cnt:=v_arr.count;
v_arr.delete(5,10);
dbms_output.put_line('元素个数:'||v_arr.count);
for i in 1..v_cnt loop
if i>=5 and i<=10 then
continue;
end if;
dbms_output.put_line('元素'||i||':'||v_arr(i));
end loop;
end;

4.exists:用于确定集合元素是否存在
declare
type ename_table_type is table of emp.ename%type;
ename_table ename_table_type:=ename_table_type();
begin
if ename_table.exists(1) then
ename_table(1):='scott';
dbms_output.put_line('元素:'||ename_table(1));
else
dbms_output.put_line('必须初始化集合元素');
end if;
end;

5.count:用于返回当前集合变量中的元素总个数.

6.limit:用于返回集合元素的最大个数.
嵌套表元素个数没有限制,无返回值
varray元素个数在定义时设置,返回varray所允许的最大元素个数
declare
type ename_table_type is varray(20) of emp.ename%type;
ename_table ename_table_type:=ename_table_type();
begin
dbms_output.put_line('集合元素的最大个数:'||ename_table.limit);
end;
declare
type ename_table_type is table of emp.ename%type;
ename_table ename_table_type:=ename_table_type();
begin
dbms_output.put_line('集合元素的最大个数:'||ename_table.limit);
end;

7.first和last:
first 用于返回集合变量第一个元素的下标,
last 用于返回集合变量最后一个元素的下标
declare
type ename_table_type is varray(10) of emp.ename%type;
ename_table ename_table_type:=ename_table_type('scott','smith','mary','blake');
begin
dbms_output.put_line('第一个元素的下标:'||ename_table.first);
dbms_output.put_line('最后一个元素的下标:'||ename_table.last);
dbms_output.put_line('第一个元素:'||ename_table(ename_table.first));
dbms_output.put_line('最后一个元素:'||ename_table(ename_table.last));
end;

8.prior和next:
prior 用于返回当前集合元素的前一个元素的下标,
next 用于返回当前集合元素的后一个元素的下标
declare
type ename_table_type is varray(10) of emp.ename%type;
ename_table ename_table_type:=ename_table_type('scott','smith','mary','blake');
begin
dbms_output.put_line('当前元素:'||ename_table(2));
dbms_output.put_line('当前元素的前一个元素的下标:'||ename_table.prior(2));
dbms_output.put_line('当前元素的后一个元素的下标:'||ename_table.next(2));
dbms_output.put_line('当前元素的前一个元素:'||ename_table(ename_table.prior(2)));
dbms_output.put_line('当前元素的后一个元素:'||ename_table(ename_table.next(2)));
end;


处理单行单列数据,可以使用标量变量(varchar2、number、date、boolean);
处理单行多列的数据,可以使用pl/sql记录(%rowtype,record);
处理单列多行数据,可以使用pl/sql集合(table、varray);
那么处理多列多行数据,怎么办呢?

四、集合类型的嵌套(联合)
1.table和%rowtype的联合
declare
type type_tab_ename is table of emp%rowtype;
v_arr type_tab_ename:=type_tab_ename();
v_cnt number:=1;
begin
for rs in (select * from emp) loop
v_arr.extend;
v_arr(v_cnt):=rs;
dbms_output.put_line('雇员名:'||v_arr(v_cnt).ename);
v_cnt:=v_cnt+1;
end loop;
end;

2.二维数组 varray和varray的嵌套
declare
type d1_varray_type is varray(10) of number;--定义一维VARRAY
type nd1_varray_type is varray(5) of d1_varray_type;--定义二维VARRAY集合
--初始化二维集合变量
v_arr nd1_varray_type:=nd1_varray_type(
d1_varray_type(2,4),
d1_varray_type(5,73));
begin
dbms_output.put_line('显示二维数组所有元素');
for i in 1..v_arr.count loop
for j in 1..v_arr(i).count loop
dbms_output.put_line('v_arr('||i||','||j||')='||v_arr(i)(j));
end loop;
end loop;
end;

(2,4)
(5,73)

如果多维集合的元素个数没有限制,那么可以在嵌套表类型中嵌套另一个嵌套表类型
3.二维嵌套表 table和table的嵌套
declare
type d1_table_type is table of number;--定义一维嵌套表
type nd1_table_type is table of d1_table_type;--定义二维嵌套表集合
--初始化二维集合变量
v_tab nd1_table_type:=nd1_table_type(
d1_table_type(58,100,102),
d1_table_type(55,6),
d1_table_type(2));
begin
dbms_output.put_line('显示二维数组所有元素');
for i in 1..v_tab.count loop
for j in 1..v_tab(i).count loop
dbms_output.put_line('v_tab('||i||','||j||')='||v_tab(i)(j));
end loop;
end loop;
end;

五、集合间的赋值:=
1.赋值符:=
将一个集合的数据赋值个另一个集合
源集合和目标集合的数据类型必须完全一致.
declare
type name_varray_type is varray(4) of varchar2(10);
name_array1 name_varray_type;
name_array2 name_varray_type;
begin
name_array1:=name_varray_type('scott','smith');
name_array2:=name_varray_type('a','a','a','a');
dbms_output.put_line('name_array2的原数据:');
for i in 1..name_array2.count loop
dbms_output.put(' '||name_array2(i));
end loop;
dbms_output.new_line;
name_array2:=name_array1; --用:=符号赋值
dbms_output.put_line('name_array2的新数据:');
for i in 1..name_array2.count loop
dbms_output.put(' '||name_array2(i));
end loop;
dbms_output.new_line;
end;

2.给集合赋NULL值:
清空集合变量的所有数据(集合方法DETELE,TRIM也可以)
declare
type name_varray_type is varray(4) of varchar2(10);
name_array name_varray_type;
name_empty name_varray_type;
begin
name_array:=name_varray_type('scott','smith');
dbms_output.put_line('name_array的原有元素个数:'||name_array.count);
name_array:=name_empty;
if name_array is null then
dbms_output.put_line('name_array的现有元素个数:0');
end if;
end;

六、嵌套表特有操作:
set:用于取消嵌套表中的重复值.
multiset union:用于取得两个嵌套表的并集(可重)
multiset union distinct:用于取得两个嵌套表的并集(distinct)。
multiset intersect:用于取得两个嵌套表的交集.
nultiset except:用于取得两个嵌套表的差集
1.set:
用于取消特定嵌套表中的重复值.
declare
type nt_table_type is table of number;
v_tab nt_table_type:=nt_table_type(2,4,3,1,2);
r_tab nt_table_type;
begin
r_tab:=set(v_tab);
for i in 1..r_tab.count loop
dbms_output.put_line('r_tab'||i||':'||r_tab(i));
end loop;
end;

2.multiset union:
用于获取两个嵌套表的并集。
两个嵌套表都有的元素在结果集中会重复。
declare
type nt_table_type is table of number;
nt1 nt_table_type:=nt_table_type(1,2,3);
nt2 nt_table_type:=nt_table_type(3,4,5);
result nt_table_type;
begin
result:=nt1 multiset union nt2;
for i in 1..result.count loop
dbms_output.put_line('result'||i||':'||result(i));
end loop;
end;
3.multiset union distinct:
用于获取两个嵌套表的并集。
两个嵌套表都有的元素在结果集中不会重复。
declare
type nt_table_type is table of number;
nt1 nt_table_type:=nt_table_type(1,2,3);
nt2 nt_table_type:=nt_table_type(3,4,5);
result nt_table_type;
begin
result:=nt1 multiset union distinct nt2;
for i in 1..result.count loop
dbms_output.put_line('result'||i||':'||result(i));
end loop;
end;
4.multiset intersect
用于获取两个嵌套表的交集
declare
type nt_table_type is table of number;
nt1 nt_table_type:=nt_table_type(1,2,3);
nt2 nt_table_type:=nt_table_type(3,4,5);
result nt_table_type;
begin
result:=nt1 multiset intersect nt2;
for i in 1..result.count loop
dbms_output.put_line('result'||i||':'||result(i));
end loop;
end;
5.multiset except
用于获取两个嵌套表的差集。
即在第一个嵌套表中存在,但在第二个嵌套表不存在的元素。
declare
type nt_table_type is table of number;
nt1 nt_table_type:=nt_table_type(1,2,3);
nt2 nt_table_type:=nt_table_type(3,4,5);
result nt_table_type;
begin
result:=nt1 multiset except nt2;
for i in 1..result.count loop
dbms_output.put_line('result'||i||':'||result(i));
end loop;
end;

操作符is empty: 用于检测嵌套表是否为null。
操作符=和!=: 用于检测两个嵌套表是否相同。
函数cardinality:用于返回嵌套表变量的元素个数
submultiset of: 用于确定一个嵌套表是否为另一个嵌套表的子集
操作符member of:用于检测特定数据是否为嵌套表元素
操作符is a set: 用于检测嵌套表是否包含重复的元素值
6.is empty:
用于检测嵌套表是否为null。
即检测嵌套表是否没有元素。
declare
type name_table_type is table of varchar2(10);
name_table name_table_type:=name_table_type();
begin
if name_table is empty then
dbms_output.put_line('嵌套表为空');
else
dbms_output.put_line('嵌套表中有元素');
end if;
end;
7.=和!=
用于检测两个嵌套表是否相同。
declare
type name_table_type is table of varchar2(10);
name_table1 name_table_type;
name_table2 name_table_type;
begin
name_table1:=name_table_type('scott');
name_table2:=name_table_type('smith');
if name_table1=name_table2 then
dbms_output.put_line('两个嵌套表完全相同');
else
dbms_output.put_line('两个嵌套表数值不同');
end if;
end;
8.cardinality
用于返回嵌套表中的元素个数。
declare
type nt_table_type is table of number;
nt1 nt_table_type:=nt_table_type(1,2,3,1);
begin
dbms_output.put_line('元素个数:'||cardinality(nt1));
end;
9.submultiset of
用于确定一个嵌套表是否为另一个嵌套表的子集.
declare
type nt_table_type is table of number;
nt1 nt_table_type:=nt_table_type(1,2,3);
nt2 nt_table_type:=nt_table_type(1,2,3,4);
begin
if nt1 submultiset of nt2 then
dbms_output.put_line('nt1是nt2的子集');
end if;
end;
10.member of
用于检测特定数据是否为嵌套表的元素.
declare
type nt_table_type is table of number;
nt1 nt_table_type:=nt_table_type(1,2,3,4);
v1 number:=&v1;
begin
if v1 member of nt1 then
dbms_output.put_line(v1||'是嵌套表nt1的元素');
else
dbms_output.put_line(v1||'不是嵌套表nt1的元素');
end if;
end;
11.is a set
用于检测嵌套表是否包含重复的元素值
declare
type nt_table_type is table of number;
nt1 nt_table_type:=nt_table_type(1,2,3,5);
begin
if nt1 is a set then
dbms_output.put_line('嵌套表无重复值');
else
dbms_output.put_line('嵌套表有重复值');
end if;
end;


七、作业
1.做一个split函数,功能如下:
输入'张三、李四、王五'
调用函数时输出多行,显示如下:
create or replace type type_table_varchar2 is table of varchar2(20);
create or replace function split(p_var varchar2,p_fengefu varchar2) return type_table_varchar2
is
v_rs type_table_varchar2:=type_table_varchar2();
begin
XXXX
return v_rs;
end;

select column_value from table(split('张三、李四、王五','、'));
张三
李四
王五
2.做一个匿名块,对数组进行排序,并显示排序后的元素。比如:
排序前数组元素顺序为:13 25 7 18 2 6 9 12
排序后数组元素顺序为:2 6 7 9 12 13 18 25