6 集合类型
6.1 数组
定义:由其元素的最大数目限定的单维有限集合,存放2GB(2*1024*1024*1024)个元素,排列是紧密的
(1)数组的定义、声明、初始化
A 数字类型的数组类型
declare
type num_varray is varray(5) of number;
v_numvarray num_varray:=num_varray(10,20,30,40); --数组的声明+初始化
begin
for idx in 1..4 loop
dbms_output.put_line(to_char(v_numvarray(idx)));
end loop;
end;
也可以在begin后面对数组进行初始化
declare
type num_varray is varray(5) of number;
v_numvarray num_varray; --只是进行声明
begin
v_numvarray:=num_varray(10,20,30,40); --在begin后面对数组进行初始化,注意,不能写在declare部分
for idx in 1..4 loop
dbms_output.put_line(to_char(v_numvarray(idx)));
end loop;
end;
B 字符串类型的数组类型
declare
G type char_varray is varray(5) of varchar2(10);
v_charvarray char_varray:=char_varray('10','20','30','40');
begin
for idx in 1..4 loop
dbms_output.put_line(v_charvarray(idx));
end loop;
end;
C 存储记录
--数组的扩展式赋值
declare
type hrc_org_rec is record(hrc_org_id number,hrc_descr varchar2(20),org_short_name varchar2(30));
type num_varray is varray(50) of hrc_org_rec;
cursor csr_hrc_org is select o.org_id,h.hrc_descr,o.org_short_name
from org_tab o,hrc_tab h
where o.hrc_code=h.hrc_code;
v_numvarray num_varray:=num_varray(null);
i integer:=1;
begin
for idx in csr_hrc_org loop
v_numvarray(i):=idx;
v_numvarray.extend; --数组往后扩展一个位置
i:=i+1;
end loop;
for idx in 1..v_numvarray.count loop
dbms_output.put_line(to_char(v_numvarray(idx).hrc_org_id));
dbms_output.put_line(v_numvarray(idx).hrc_descr);
dbms_output.put_line(v_numvarray(idx).org_short_name);
end loop;
end;
--统一初始化数组
declare
type hrc_org_rec is record(hrc_org_id number,hrc_descr varchar2(20),org_short_name varchar2(30));
type num_varray is varray(50) of hrc_org_rec;
cursor csr_hrc_org is select o.org_id,h.hrc_descr,o.org_short_name
from org_tab o,hrc_tab h
where o.hrc_code=h.hrc_code;
v_numvarray num_varray:=num_varray(null,null,null,null,null,null);
i integer:=1;
begin
for idx in csr_hrc_org loop
v_numvarray(i):=idx;
i:=i+1;
end loop;
for idx in 1..v_numvarray.count loop
dbms_output.put_line(to_char(v_numvarray(idx).hrc_org_id));
dbms_output.put_line(v_numvarray(idx).hrc_descr);
dbms_output.put_line(v_numvarray(idx).org_short_name);
end loop;
end;
(2) NULL数组和NULL元素
NULL数组 -- 数组还没有被初始化
NULL元素 -- 数组已经被初始化,但是里面的元素的值是NULL
A NULL数组,数组可以用 is null 来判断是否为空的
declare
type num_varray is varray(5) of number;
v_numvarray num_varray;
begin
if v_numvarray is null then
dbms_output.put_line('NULL');
end if;
end;
declare
type num_varray is varray(5) of number;
v_numvarray num_varray;
begin
v_numvarray:=null; --记录是不能这样赋值的,而数组可以
if v_numvarray is null then
dbms_output.put_line('NULL');
end if;
end;
B NULL元素
declare
type num_varray is varray(5) of number;
v_numvarray num_varray;
begin
v_numvarray:=num_varray(null);
if v_numvarray is null then
dbms_output.put_line('NULL');
else
dbms_output.put_line('NOT NULL');
end if;
end;
declare
type num_varray is varray(5) of number;
v_numvarray num_varray;
begin
v_numvarray:=num_varray(null);
if v_numvarray is null then
dbms_output.put_line('NULL');
else
dbms_output.put_line('NOT NULL');
end if;
if v_numvarray(1) is null then
dbms_output.put_line('element is null!');
end if;
end;
总结:
v_numvarray:=num_varray(null); --初始化数组,第一个元素为null
v_numvarray:=null; --将整个数组置为NULL,相当于未初始化
(3) 访问数组的元素,通过数组的索引下标,数组的下标是从1开始的
declare
type num_varray is varray(5) of number;
v_numvarray num_varray:=num_varray(10,20,30,40); --数组的声明+初始化
begin
for idx in 1..4 loop
dbms_output.put_line(to_char(v_numvarray(idx)));
end loop;
end;
A 没有定义的元素是否能访问?
declare
type num_varray is varray(5) of number;
v_numvarray num_varray:=num_varray(10,20,30,40); --数组的声明+初始化
begin
for idx in 1..4 loop
dbms_output.put_line(to_char(v_numvarray(idx)));
end loop;
dbms_output.put_line(v_numvarray(5)); --报错,没有的定义的元素是不能访问的,ORA-06533:Subscript beyond count
end;
B 长度越界
declare
type num_varray is varray(5) of number;
v_numvarray num_varray:=num_varray(10,20,30,40,50,60); --报错,长度不能够越界,
--ORA-06532:Subscript outside of limit
begin
for idx in 1..4 loop
dbms_output.put_line(to_char(v_numvarray(idx)));
end loop;
dbms_output.put_line(v_numvarray(5));
dbms_output.put_line(v_numvarray(6));
end;
(4)和index-by表的区别
declare
type num_table is table of number index by binary_integer;
v_example_tab num_table:=num_table(1,2,3,4,5,6); --这种赋值是错误
v_num number:=13;
begin
for idx in 1..6 loop
dbms_output.put_line(to_char(v_example_tab(idx)));
end loop;
end;
declare
type num_table is table of number index by binary_integer;
v_example_tab num_table:=null; --不能够赋予NULL值,但是可以给元素赋null值
v_num number:=13;
begin
for idx in 1..6 loop
dbms_output.put_line(to_char(v_example_tab(idx)));
end loop;
end;
declare
type num_table is table of number;
v_numtable num_table:=num_table(10,20,30,40);
begin
for idx in 1..v_numtable.count loop
dbms_output.put_line(to_char(v_numtable(idx)));
end loop;
end;
可以给index-by表的元素赋NULL值
declare
type num_table is table of number index by binary_integer;
v_example_tab num_table;
v_num number:=13;
begin
for idx in 1..6 loop
v_example_tab(idx):=null;
end loop;
for idx in 1..6 loop
if v_example_tab(idx) is null then
dbms_output.put_line('NULL');
end if;
end loop;
end;
declare
type num_table is table of number index by binary_integer;
v_example_tab num_table;
begin
if v_example_tab is null then --不能判断是否为NULL,这种判断没意义,尽管没有报错
dbms_output.put_line('NULL');
else
dbms_output.put_line('NOT NULL');
end if;
end;
declare
type num_varray is varray(5) of number;
v_numvarray num_varray:=num_varray(10,20,30,40,50,60); --数组不能长度越界,但是index-by不存在界限的问题
begin
for idx in 1..4 loop
dbms_output.put_line(to_char(v_numvarray(idx)));
end loop;
dbms_output.put_line(v_numvarray(5));
dbms_output.put_line(v_numvarray(6));
end;
###########################################################################################
6.2 嵌套表
定义:由其元素的没有最大数目限制的单维集合,无上限,排列是紧密的
(1) 定义、声明、初始化
declare
type num_table is table of number; --定义嵌套表的方式,与数组类似
v_numtable num_table:=num_table(10,20,30,40);
begin
for idx in 1..v_numtable.count loop
dbms_output.put_line(to_char(v_numtable(idx))); --下标也是从1开始的,访问跟数组是一样的
end loop;
end;
(2) NULL嵌套表和NULL嵌套表元素
A NULL嵌套表
declare
type num_table is table of number;
v_numtable num_table;
begin
if v_numtable is null then
dbms_output.put_line('NULL');
end if;
end;
B NULL嵌套表元素
declare
type num_table is table of number;
v_numtable num_table;
begin
v_numtable:=num_table(null);
if v_numtable is null then
dbms_output.put_line('NULL');
else
dbms_output.put_line('NOT NULL');
end if;
if v_numtable(1) is null then
dbms_output.put_line('element is null!');
end if;
end;
(3)访问嵌套表的元素 -- 跟数组一样
declare
type num_table is table of number;
v_numtable num_table:=num_table(10,20,30,40);
begin
for idx in 1..v_numtable.count loop
dbms_output.put_line(to_char(v_numtable(idx)));
end loop;
end;
###########################################################################################
6.3 集合的方法
集合的方法:数组跟嵌套表都是可以用的,除非特别指出
(1) exists方法 -- 返回集合中索引对应位置是否有元素的存在,存在就返回true,不存在返回false
declare
type num_varray is varray(5) of number;
v_numvarray num_varray:=num_varray(10,20,30,40);
begin
if v_numvarray.exists(5) then
dbms_output.put_line('YES');
else
dbms_output.put_line('NO');
end if;
end;
(2) count方法 -- 返回集合元素的数目,包括NULL值
declare
type num_varray is varray(5) of number;
v_numvarray num_varray:=num_varray(10,20,30,40,null);
begin
dbms_output.put_line(to_char(v_numvarray.count)); --5个
end;
(3) limit方法 -- 只有数组有这个方法,嵌套表是没有的,输出数组的上限
declare
type num_varray is varray(5) of number;
v_numvarray num_varray:=num_varray(10,20,30,40);
begin
dbms_output.put_line(to_char(v_numvarray.limit)); --输出上限
end;
(4) first、last 方法,返回的是索引号,而不是第一个值
first -- 返回集合中第一个有值的元素(包含null值)的索引号,如果集合为空返回null
last -- 返回集合中最后一个有值的元素(包含null值)的索引号,如果集合为空返回null
declare
type num_varray is varray(5) of number;
v_numvarray num_varray:=num_varray(10,null,30,null);
begin
dbms_output.put_line(to_char(v_numvarray.first)); --输出的是1
dbms_output.put_line(to_char(v_numvarray.last)); --输出的是4
end;
(5) next,prior方法
next -- 返回集合中指定元素下一个有值的元素(包含null值)的索引号,如果集合为空返回null
prior -- 返回集合中指定元素上一个有值的元素(包含null值)的索引号,如果集合为空返回null
declare
type num_varray is varray(5) of number;
v_numvarray num_varray:=num_varray(10,null,30,null);
begin
dbms_output.put_line(to_char(v_numvarray.next(1))); --输出的是2
dbms_output.put_line(to_char(v_numvarray.next(4))); --输出的是空
end;
(6) extend方法 -- 在集合的末尾处添加新元素或者在末尾处初始化元素
数组:
declare
type num_varray is varray(200) of number;
v_numvarray num_varray:=num_varray(null,null);
begin
dbms_output.put_line(to_char(v_numvarray.count));
v_numvarray(1):=1001;
v_numvarray(2):=1002;
v_numvarray.extend; --在集合的末尾处添加一个null元素
dbms_output.put_line(to_char(v_numvarray.count));
v_numvarray(3):=1003;
v_numvarray.extend(5); --在集合的末尾处添加5个null元素
dbms_output.put_line(to_char(v_numvarray.count));
v_numvarray(8):=1008;
v_numvarray.extend(5,8); --在集合的末尾处添加5个在索引8处的元素
dbms_output.put_line(to_char(v_numvarray.count));
for i in 1..v_numvarray.count loop
if v_numvarray(i) is not null then
dbms_output.put_line(to_char(v_numvarray(i)));
else
dbms_output.put_line('NULL!');
end if;
end loop;
end;
A 直接extend --在末尾处添加一个null元素
B extend(5) --在末尾处添加5个null元素
C extend(5,8) --在集合的末尾处添加5个在索引8处的元素
嵌套表:
跟数组一模一样
(7) trim方法 -- 在集合的末尾处删除元素,后面带数字表示在末尾处删除多少个元素
declare
type num_varray is varray(200) of number;
v_numvarray num_varray:=num_varray(null,null);
begin
dbms_output.put_line(to_char(v_numvarray.count));
v_numvarray(1):=1001;
v_numvarray(2):=1002;
v_numvarray.trim; --在集合的末尾处删除一个元素
dbms_output.put_line(to_char(v_numvarray.count));
v_numvarray.extend;
dbms_output.put_line(to_char(v_numvarray.count));
v_numvarray(2):=10022;
v_numvarray.trim(2);
dbms_output.put_line(to_char(v_numvarray.count));
v_numvarray.extend(2);
v_numvarray(1):=10011;
v_numvarray(2):=10021;
dbms_output.put_line(to_char(v_numvarray.count));
for i in 1..v_numvarray.count loop
if v_numvarray(i) is not null then
dbms_output.put_line(to_char(v_numvarray(i)));
else
dbms_output.put_line('NULL!');
end if;
end loop;
end;
(8) delete方法 -- 删除元素
只有嵌套表后面能带数字,数字表示删除数字所在位置的元素,如果是数组,只能用delete方法,后面不能带数字
declare
type num_table is table of number;
v_numtable num_table:=num_table(null,null,null,null,null);
begin
dbms_output.put_line(to_char(v_numtable.count));
v_numtable(1):=1001;
v_numtable(2):=1002;
v_numtable.delete; --直接将嵌套表的元素全部删除
dbms_output.put_line(to_char(v_numtable.count));
v_numtable.extend(2);
dbms_output.put_line(to_char(v_numtable.count));
v_numtable(1):=1001;
v_numtable(2):=1002;
dbms_output.put_line(to_char(v_numtable.count));
v_numtable.delete(2); --删除索引位置2的元素
dbms_output.put_line(to_char(v_numtable.count));
v_numtable.extend(2);
dbms_output.put_line(to_char(v_numtable.count));
v_numtable(2):=10022;
dbms_output.put_line(to_char(v_numtable.count)); --为什么会变成4了
for idx in 1..v_numtable.count loop
dbms_output.put_line(v_numtable(idx));
end loop;
end;
###########################################################################################
6.4 集合在数据库中的使用
6.4.1 数组在数据库中的使用
(1) 数组在数据库中存储
A 创建一个数组对象
SQL> create or replace type add_list as varray(10) of number(10); /
Type created.
B 查询数据字典
select * from user_objects where object_name='ADD_LIST'; --数组以type形式存在
select * from user_types;
(2) 数组的DML操作
A insert 操作
方法1:直接插入
insert into direct_address_list values('OFF101',add_list(1001,1002,1003,1004));
commit;
查询:
SQL> select * from direct_address_list;
LIST_I DIRECT_ADDRESS
------ --------------------------------------------------
OFF101 ADD_LIST(1001, 1002, 1003, 1004)
人 车 人和车的关系表
R001 C001 R001 C001
R002 C002 R001 C002
R003 C003 R002 C003
R002 C003
把车的编号作为数组
R001 ADD_LIST(C001,C002)
R002 ADD_LIST(C003)
R003 ADD_LIST(C003)
方法2:用匿名块的方式插入
declare
v_add_varray add_list:=add_list(1001,1002,1003,1004);
begin
insert into direct_address_list values('OFF102',v_add_varray);
commit;
end;
SQL> select * from direct_address_list;
LIST_I DIRECT_ADDRESS
------ --------------------------------------------------
OFF101 ADD_LIST(1001, 1002, 1003, 1004)
OFF102 ADD_LIST(1001, 1002, 1003, 1004)
B update操作
方法1:直接update
update direct_address_list set DIRECT_ADDRESS=add_list(1011,1012,1013) where list_id='OFF102';
commit;
SQL> select * from direct_address_list;
LIST_ID DIRECT_ADDRESS
-------------------- --------------------------------------------------
OFF101 ADD_LIST(1001, 1002, 1003, 1004)
OFF102 ADD_LIST(1011, 1012, 1013)
方法2:在匿名块中update
declare
v_add_varray add_list:=add_list(1011,1012,1013,1014);
begin
update direct_address_list set direct_address=v_add_varray where list_id='OFF101';
commit;
end;
SQL> select * from direct_address_list;
LIST_ID DIRECT_ADDRESS
-------------------- --------------------------------------------------
OFF101 ADD_LIST(1011, 1012, 1013, 1014)
OFF102 ADD_LIST(1011, 1012, 1013)
C delete操作,不能用数组作为where条件过滤
delete from direct_address_list where list_id='OFF102';
commit;
SQL> select * from direct_address_list;
LIST_ID DIRECT_ADDRESS
-------------------- --------------------------------------------------
OFF101 ADD_LIST(1011, 1012, 1013, 1014)
D select 操作
方法1:直接查询
SQL> select * from direct_address_list;
LIST_ID DIRECT_ADDRESS
-------------------- --------------------------------------------------
OFF101 ADD_LIST(1011, 1012, 1013, 1014)
方法2:用匿名块查询并依次输出 使用select into方法
declare
v_add_varray direct_address_list.direct_address%type; --%type也是行变量,但是只能存储一列
begin
select direct_address into v_add_varray from direct_address_list where list_id='OFF101';
for idx in 1..v_add_varray.count loop
dbms_output.put_line(to_char(v_add_varray(idx)));
end loop;
end;
declare
v_add_varray direct_address_list.direct_address%type; --%type也是行变量,但是只能存储一列
begin
for i in (select list_id from direct_address_list) loop
select direct_address into v_add_varray from direct_address_list where list_id=i.list_id;
for idx in 1..v_add_varray.count loop
dbms_output.put_line(to_char(v_add_varray(idx)));
end loop;
dbms_output.put_line('--------------');
end loop;
end;
多行输出
declare
v_add_varray direct_address_list.direct_address%type; --%type也是行变量,但是只能存储一列
begin
for i in (select list_id from direct_address_list) loop
select direct_address into v_add_varray from direct_address_list where list_id=i.list_id;
for idx in 1..v_add_varray.count loop
dbms_output.put_line(to_char(v_add_varray(idx)));
end loop;
dbms_output.put_line('--------------');
end loop;
end;
##########################################################################################
6.4.2 嵌套表在数据库中的使用
(1) 嵌套表在数据库中存储
A 创建一个嵌套表对象
create or replace type home_add_list as table of number(10);
B 查询数据字典
select * from user_objects where object_name='HOME_ADD_LIST';
TYPE就是集合类型,无法从视图上识别是哪种集合类型,需要查看创建的代码才能够看到
SQL> set long 1000; --调整select结果的长度
怎样查看一个对象的创建的代码
select dbms_metadata.get_ddl('TYPE','TYPE_NAME') from dual;
C 基于嵌套表创建表
create table home_addresses_list(list_id varchar2(6) primary key,home_addresses home_add_list)
nested table home_addresses store as home_addresses_tab;
嵌套表 作为..存储
select * from user_objects where object_name like 'HOME_ADDRESS%'; --发现上面的操作创建了两个表类型
select * from user_tables where table_name like 'HOME_ADDRESS%';
--通过这个视图,会发现HOME_ADDRESSES_TAB这个表它的nested这个字段的值为YES,说明它是一个嵌套表
(2) 嵌套表的DML操作
A insert 操作
方法1:直接insert
insert into home_addresses_list values('OFF101',home_add_list(1001,1002,1003,1004));
commit;
SQL> select * from home_addresses_list;
LIST_ID HOME_ADDRESSES
-------------------- --------------------------------------------------
OFF101 HOME_ADD_LIST(1001, 1002, 1003, 1004)
方法2:通过匿名块
declare
v_add_varray home_add_list:=home_add_list(1001,1002,1003,1004);
begin
insert into home_addresses_list values('OFF102',v_add_varray);
commit;
end;
SQL> select * from home_addresses_list;
LIST_ID HOME_ADDRESSES
-------------------- --------------------------------------------------
OFF101 HOME_ADD_LIST(1001, 1002, 1003, 1004)
OFF102 HOME_ADD_LIST(1001, 1002, 1003, 1004)
B update操作:跟数组一样
C delete操作:跟数组一样
D select操作
方法1:直接查询
SQL> select * from home_addresses_list;
LIST_ID HOME_ADDRESSES
-------------------- --------------------------------------------------
OFF101 HOME_ADD_LIST(1001, 1002, 1003, 1004)
OFF102 HOME_ADD_LIST(1001, 1002, 1003, 1004)
方法2:通过匿名块查询(和数组一样)
方法3:子查询的方法,只查询嵌套表行(直接查询嵌套表是查不到)
SQL> select * from table(select home_addresses from home_addresses_list where list_id='OFF101');
COLUMN_VALUE
------------
1001
1002
1003
1004
方法4:将嵌套表展开的方式来展现
SQL> select list_id,column_value from home_addresses_list,table(home_addresses);
LIST_ID COLUMN_VALUE
-------------------- ------------
OFF101 1001
OFF101 1002
OFF101 1003
OFF101 1004
OFF102 1001
OFF102 1002
OFF102 1003
OFF102 1004
8 rows selected.
方法5:通过创建临时表的方式对局部变量操作
A 创建一个实体表作为临时表存储嵌套表的数据
create table num_table(col number(10));
B 通过对局部变量的访问插入嵌套表数据到上面所创建的表
declare
v_add_list home_add_list:=home_add_list(1001,1002,1003);
begin
insert into num_table select column_value from table(cast(v_add_list as home_add_list));
commit;
end;
select column_value from table(cast(v_add_list as home_add_list)) --将一个嵌套表散开显示
SQL> select * from num_table;
COL
----------
1001
1002
1003
用在home_addresses_list表
declare
cursor csr_add_list is select * from home_addresses_list;
v_add_list varchar2(30);
v_home_list home_add_list;
begin
open csr_add_list;
loop
fetch csr_add_list into v_add_list,v_home_list;
exit when(csr_add_list%notfound);
insert into num_table select column_value from table(cast(v_home_list as home_add_list));
end loop;
close csr_add_list;
end;
select * from num_table;
练习6:
写一段程序,在SCOTT下
1.克隆表dept_t,从dept克隆来的,不要数据
2.给dept_t加一列,数据类型为数组,存储员工号
3.程序需要独处所有的员工号,存储在数组中,然后写入到dept_t的数组列中
create table dept_t as select * from dept where 1=2;
create or replace type emp_list as varray(100) of number;
alter table dept_t add varray_empno emp_list;
declare
cursor csr_emp is select b.deptno,b.dname,b.loc
from dept b;
v_deptno number;
v_dname varchar2(30);
v_loc varchar2(30);
v_varray emp_list:=emp_list(null);
i integer;
begin
open csr_emp;
loop
v_varray.delete;
fetch csr_emp into v_deptno,v_dname,v_loc;
exit when(csr_emp%notfound);
i:=1;
for idx in (select a.empno from emp a where a.deptno=v_deptno) loop
v_varray.extend;
v_varray(i):=idx.empno;
i:=i+1;
end loop;
insert into dept_t values(v_deptno,v_dname,v_loc,v_varray);
commit;
end loop;
close csr_emp;
exception when others then
null;
end;
SQL> select * from dept_t;
DEPTNO DNAME LOC VARRAY_EMPNO
---------- -------------- ------------- --------------------------------------------------
10 ACCOUNTING NEW YORK EMP_LIST(7782, 7839, 7934)
20 RESEARCH DALLAS EMP_LIST(7369, 7566, 7788, 7876, 7902)
30 SALES CHICAGO EMP_LIST(7499, 7521, 7654, 7698, 7844, 7900)
40 OPERATIONS BOSTON EMP_LIST()
###########################################################################################
6.5 数组、嵌套表、index-by区别
| 数组 | 嵌套表 | index-by表
未初始化状态 | NULL数组 | NULL嵌套表 | 不能做null index-by
判断为空的方式 | is null | is null | 不能整体判断
NULL元素 | 可以赋null值 | 可以赋null值 | 可以赋null值
赋值方式 | list方式赋值 | list方式赋值 | 每个索引位置赋值
元素的维度 | 单维 | 单维 | 多维
元素个数 | 2GB个 | 无穷大 | 无穷大
稀疏/紧密 | 紧密 | 紧密 | 稀疏
类型存储 | 可存数据库 | 可存数据库 | 不可存储,临时定义临时使用
列存储方式 | 数组列 | 嵌套表列 | 不存在
存储复合数据类型 | 可以存储记录、行变量、列变量 | 可以存储记录、行变量、列变量 | 可以存储记录、行变量、列变量
使用:
如果临时使用,不需要创建存储,用index-by表,如果对象是多维的,只能用index-by
如果经常使用,或者作为表列类型,且集合元素有上限,且集合元素需要存储在单独的表对象中,用数组
如果经常使用,或者作为表列类型,且集合元素无上限,且集合元素需要存储在单独的表对象中,用嵌套表