通常我们获取游标数据是用
fetch some_cursor into var1, var2 的形式,自 Oracle 8i 起,Oracle 为我们提供了
fetch bulk
collect 来批量取游标中的数据,它能在读取游标中大量数据的时候提升效率,采用bulk
collect可以将查询结果一次性地加载到collections中。而不是通过cursor一条一条地处理。
Bulk
Collect批查询在某种程度上可以提高查询效率,它首先将所需数据读入内存,然后再统计分析,这样就可以提高查询效率。但是,如果Oracle数据库的内存较小,Shared
Pool Size不足以保存Bulk Collect批查询结果,那么该方法需要将Bulk
Collect的集合结果保存在磁盘上,在这种情况下,Bulk Collect方法的效率会较低;
fetch bulk
collect into 的使用格式是:fetch some_cursor bulk collect into col1, col2
limit xxx。col1、col2 是声明的集合类型变量,xxx 为每次取数据块的大小(记录数),相当于缓冲区的大小,可以不指定
limit xxx 大小。
测试实例:
--
1000000行数据使用批量fetch方式,执行时间1s
declare
type id_type is table of
emp.EMPNO%type;
v_id
id_type;
type name_type is table of
emp.ENAME%type;
v_name
name_type;
type sal_type is table of
emp.SAL%type;
v_sal
sal_type;
cursor all_emp
is
select empno,ename,sal from emp2
where rownum <= 1000000;
begin
open
all_emp;
loop
fetch all_emp bulk collect into
v_id,v_name,v_sal limit
256;
for i in 1..v_id.count
loop
null;
end
loop;
exit when
all_emp%notfound;
end
loop;
close all_emp;
end;
--1000000行数据使用逐行fetch方式执行时间11s
declare
v_id
emp2.EMPNO%type;
v_name
emp2.ename%type;
v_sal
emp2.sal%type;
cursor all_emp
is
select empno,ename,sal from emp2
where rownum <=
1000000;
begin
open
all_emp;
loop
fetch all_emp into
v_id,v_name,v_sal;
exit when
all_emp%notfound;
null;
end
loop;
close all_emp;
end;
几点说明:
1、 从测试结果来看游标的记录数越大时,用 fetch bulk
collect into 的效率很明显示,趋于很小时就差不多了。
2、 可以在select into,fetch
into,returning into语句使用bulk collect。
3、 在使用bulk
collect时,所有的into变量都必须是collections
4、 在这个例子中,我们借助于集合的 first、last
、count 来进行遍历。
5、 关于 limit 参数
6、 你可以根据你的实际来调整 limit
参数的大小,来达到你最优的性能。limit 参数会影响到 pga 的使用率。而且也可以在 fetch bulk 中省略 limit
参数,写成
fetch all_contacts_cur bulk collect into
v_contacts;
不写 limit 参数时,可以去除外层循环,begin-end 部分可写成:
begin
open
all_emp;
fetch all_emp bulk collect into
v_id,v_name,v_sal;
for i in 1..v_id.count
loop
null;
end
loop;
close all_emp;
end;
除了Bulk
Collect批查询外,我们还可以使用FORALL语句来实现批插入、删除和更新,这在大批量数据操作时可以显著提高执行效率
使用FORALL比FOR效率高,因为前者只切换一次上下文,而后者将是在循环次数一样多个上下文间切换。
--FOR用法 ,9s
DECLARE
TYPE ID_TYPE IS TABLE OF
EMP.EMPNO%TYPE;
V_ID
ID_TYPE;
TYPE NAME_TYPE IS TABLE OF
EMP.ENAME%TYPE;
V_NAME
NAME_TYPE;
TYPE SAL_TYPE IS TABLE OF
EMP.SAL%TYPE;
V_SAL
SAL_TYPE;
CURSOR ALL_EMP
IS
SELECT EMPNO,ENAME,SAL FROM EMP2
WHERE ROWNUM <= 200000;
BEGIN
OPEN
ALL_EMP;
FETCH ALL_EMP BULK COLLECT INTO
V_ID,V_NAME,V_SAL;
FOR I IN 1..V_ID.COUNT
LOOP
insert into t(id)
values(V_ID(i));
END
LOOP;
CLOSE ALL_EMP;
END;
--FORALL用法 ,5S
DECLARE
TYPE ID_TYPE IS TABLE OF
EMP.EMPNO%TYPE;
V_ID
ID_TYPE;
TYPE NAME_TYPE IS TABLE OF
EMP.ENAME%TYPE;
V_NAME
NAME_TYPE;
TYPE SAL_TYPE IS TABLE OF
EMP.SAL%TYPE;
V_SAL
SAL_TYPE;
CURSOR ALL_EMP
IS
SELECT EMPNO,ENAME,SAL FROM EMP2
WHERE ROWNUM <= 200000;
BEGIN
OPEN
ALL_EMP;
FETCH ALL_EMP BULK COLLECT INTO
V_ID,V_NAME,V_SAL
;
FORALL I IN
1..V_ID.COUNT
insert into t(id)
values(V_ID(i));
CLOSE ALL_EMP;
END;