Oracle数据库之FORALL与BULK COLLECT语句
Oracle数据库之FORALL与BULK COLLECT语句
我们再来看一下PL/SQL块的执行过程:当PL/SQL运行时引擎处理一块代码时,它使用PL/SQL引擎来执行过程化的代码,而将SQL语句发送给SQL引擎来执行;SQL引擎执行完毕后,将结果再返回给PL/SQL引擎。这种在PL/SQL引擎和SQL引擎之间的交互,称为上下文交换(context switch)。每发生一次交换,就会带来一定的额外开销。下面是一个示意图:
从Oracle 8i开始,PL/SQL得到了两点增强,可以将PL/SQL引擎和SQL引擎之间的多次上下文交换压缩为一次交换:
- FORALL,用于增强PL/SQL引擎到SQL引擎的交换。
- BULK COLLECT,用于增强SQL引擎到PL/SQL引擎的交换。(前面我们已经介绍过了)
1. FORALL介绍
使用FORALL,可以将多个DML批量发送给SQL引擎来执行,最大限度地减少上下文交互所带来的开销。下面是 FORALL 的一个示意图:
语法:
FORALL index IN
{ lower_bound .. upper_bound
| INDICES OF collection_name [ BETWEEN lower_bound AND upper_bound ]
| VALUES OF index_collection
}
[ SAVE EXCEPTIONS ] dml_statement;
说明:
index_name:一个无需声明的标识符,作为集合下标使用。
lower_bound .. upper_bound:数字表达式,来指定一组连续有效的索引数字下限和上限。该表达式只需解析一次。
INDICES OF collection_name:用于指向稀疏数组的实际下标。跳过没有赋值的元素,例如被 DELETE 的元素,NULL 也算值。
VALUES OF index_collection_name:把该集合中的值当作下标,且该集合值的类型只能是 PLS_INTEGER/BINARY_INTEGER。
SAVE EXCEPTIONS:可选关键字,表示即使一些DML语句失败,直到FORALL LOOP执行完毕才抛出异常。可以使用SQL%BULK_EXCEPTIONS 查看异常信息。
dml_statement:静态语句,例如:UPDATE或者DELETE;或者动态(EXECUTE IMMEDIATE)DML语句。
2. FORALL的使用
示例所使用表结构:
create table tmp_tab(
id number(5),
name varchar2(50)
);
示例1,使用FORALL批量插入、修改、删除数据:
--批量插入
declare
-- 定义索引表类型
type tb_table_type is table of tmp_tab%rowtype index by binary_integer;
tb_table tb_table_type;
begin
for i in 1..100 loop
tb_table(i).id:=i;
tb_table(i).name:='NAME'||i;
end loop; forall i in 1..tb_table.count
insert into tmp_tab values tb_table(i);
end;
--批量修改
declare
type tb_table_type is table of tmp_tab%rowtype index by binary_integer;
tb_table tb_table_type;
begin
for i in 1..100 loop
tb_table(i).id:=i;
tb_table(i).name:='MY_NAME_'||i;
end loop;
forall i in 1..tb_table.count
update tmp_tab t set row = tb_table(i) where t.id = tb_table(i).id;
end;
--批量删除
declare
type tb_table_type is table of tmp_tab%rowtype index by binary_integer;
tb_table tb_table_type;
begin
for i in 1..10 loop
tb_table(i).id:=i;
tb_table(i).name:='MY_NAME_'||i;
end loop;
forall i in 1..tb_table.count
delete from tmp_tab where id = tb_table(i).id;
end;
示例2,使用INDICES OF子句:
declare
type demo_table_type is table of tmp_tab%rowtype index by binary_integer;
demo_table demo_table_type;
begin
for i in 1..10 loop
demo_table(i).id:=i;
demo_table(i).name:='NAME'||i;
end loop;
-- 使用集合的delete方法移除第3、6、9三个成员
demo_table.delete(3);
demo_table.delete(6);
demo_table.delete(9);
forall i in indices of demo_table
insert into tmp_tab values demo_table(i);
end;
示例3,使用VALUES OF子句:
declare
type index_poniter_type is table of pls_integer;
index_poniter index_poniter_type;
type demo_table_type is table of tmp_tab%rowtype index by binary_integer;
demo_table demo_table_type;
begin
index_poniter := index_poniter_type(1,3,5,7);
for i in 1..10 loop
demo_table(i).id:=i;
demo_table(i).name:='NAME'||i;
end loop;
forall i in values of index_poniter
insert into tmp_tab values demo_table(i);
end;
3. FORALL注意事项
使用FORALL时,应该遵循如下规则:
- FORALL语句的执行体,必须是一个单独的DML语句,比如INSERT,UPDATE或DELETE。
- 不要显式定义index_row,它被PL/SQL引擎隐式定义为PLS_INTEGER类型,并且它的作用域也仅仅是FORALL。
- 这个DML语句必须与一个集合的元素相关,并且使用FORALL中的index_row来索引。注意不要因为index_row导致集合下标越界。
- lower_bound和upper_bound之间是按照步进 1 来递增的。
- 在sql_statement中,不能单独地引用集合中的元素,只能批量地使用集合。
- 在sql_statement中使用的集合,下标不能使用表达式。
4. BULK COLLECT介绍
BULK COLLECT子句会批量检索结果,即一次性将结果集绑定到一个集合变量中,并从SQL引擎发送到PL/SQL引擎。
通常可以在SELECT INTO、FETCH INTO以及RETURNING INTO子句中使用BULK COLLECT。下面逐一描述BULK COLLECT在这几种情形下的用法。
5. BULK COLLECT的使用
5.1 在SELECT INTO中使用BULK COLLECT
示例:
DECLARE
-- 定义记录类型
TYPE emp_rec_type IS RECORD
(
empno emp.empno%TYPE,
ename emp.ename%TYPE,
hiredate emp.hiredate%TYPE
);
-- 定义基于记录的嵌套表
TYPE nested_emp_type IS TABLE OF emp_rec_type;
-- 声明变量
emp_tab nested_emp_type;
BEGIN
-- 使用BULK COLLECT将所得的结果集一次性绑定到记录变量emp_tab中
SELECT empno, ename, hiredate
BULK COLLECT INTO emp_tab
FROM emp; FOR i IN emp_tab.FIRST .. emp_tab.LAST LOOP
DBMS_OUTPUT.PUT_LINE('当前记录: '
||emp_tab(i).empno||chr(9)
||emp_tab(i).ename||chr(9)
||emp_tab(i).hiredate);
END LOOP;
END;
说明:使用BULK COLLECT一次即可提取所有行并绑定到记录变量,这就是所谓的批量绑定。
5.2 在FETCH INTO中使用BULK COLLECT
在游标中可以使用BLUK COLLECT一次取出一个数据集合,比用游标单条取数据效率高,尤其是在网络不大好的情况下。
语法:
FETCH ... BULK COLLECT INTO ...[LIMIT row_number];
- 1
在使用BULK COLLECT子句时,对于集合类型会自动对其进行初始化以及扩展。因此如果使用BULK COLLECT子句操作集合,则无需对集合进行初始化以及扩展。由于BULK COLLECT的批量特性,如果数据量较大,而集合在此时又自动扩展,为避免过大的数据集造成性能下降,因此可以使用LIMIT子句来限制一次提取的数据量。LIMIT子句只允许出现在FETCH操作语句的批量中。
示例:
DECLARE
CURSOR emp_cur IS
SELECT empno, ename, hiredate FROM emp; TYPE emp_rec_type IS RECORD
(
empno emp.empno%TYPE,
ename emp.ename%TYPE ,
hiredate emp.hiredate%TYPE
);
-- 定义基于记录的嵌套表
TYPE nested_emp_type IS TABLE OF emp_rec_type;
-- 声明集合变量
emp_tab nested_emp_type;
-- 定义了一个变量来作为limit的值
v_limit PLS_INTEGER := 5;
-- 定义变量来记录FETCH次数
v_counter PLS_INTEGER := 0;
BEGIN
OPEN emp_cur; LOOP
-- fetch时使用了BULK COLLECT子句
FETCH emp_cur
BULK COLLECT INTO emp_tab
LIMIT v_limit; -- 使用limit子句限制提取数据量 EXIT WHEN emp_tab.COUNT = 0; -- 注意此时游标退出使用了emp_tab.COUNT,而不是emp_cur%notfound
v_counter := v_counter + 1; -- 记录使用LIMIT之后fetch的次数 FOR i IN emp_tab.FIRST .. emp_tab.LAST
LOOP
DBMS_OUTPUT.PUT_LINE( '当前记录: '
||emp_tab(i).empno||CHR(9)
||emp_tab(i).ename||CHR(9)
||emp_tab(i).hiredate);
END LOOP;
END LOOP; CLOSE emp_cur; DBMS_OUTPUT.put_line( '总共获取次数为:' || v_counter );
END;
5.3 在RETURNING INTO中使用BULK COLLECT
BULK COLLECT除了与SELECT,FETCH进行批量绑定之外,还可以与INSERT,DELETE,UPDATE语句结合使用。当与这几个DML语句结合时,需要使用RETURNING子句来实现批量绑定。
示例:
DECLARE
TYPE emp_rec_type IS RECORD
(
empno emp.empno%TYPE,
ename emp.ename%TYPE,
hiredate emp.hiredate%TYPE
);
TYPE nested_emp_type IS TABLE OF emp_rec_type;
emp_tab nested_emp_type;
BEGIN
DELETE FROM emp WHERE deptno = 20
RETURNING empno, ename, hiredate -- 使用returning 返回这几个列
BULK COLLECT INTO emp_tab; -- 将返回的列的数据批量插入到集合变量 DBMS_OUTPUT.put_line( '删除 ' || SQL%ROWCOUNT || ' 行记录' );
COMMIT; IF emp_tab.COUNT > 0 THEN -- 当集合变量不为空时,输出所有被删除的元素
FOR i IN emp_tab.FIRST .. emp_tab.LAST LOOP
DBMS_OUTPUT.PUT_LINE('当前记录:'
|| emp_tab( i ).empno || CHR( 9 )
|| emp_tab( i ).ename || CHR( 9 )
|| emp_tab( i ).hiredate
|| ' 已被删除' );
END LOOP;
END IF;
END;
6. BULK COLLECT的注意事项
- BULK COLLECT INTO 的目标对象必须是集合类型。
- 只能在服务器端的程序中使用BULK COLLECT,如果在客户端使用,就会产生一个不支持这个特性的错误。
- 不能对使用字符串类型作键的关联数组使用BULK COLLECT子句。
- 复合目标(如对象类型)不能在RETURNING INTO子句中使用。
- 如果有多个隐式的数据类型转换的情况存在,多重复合目标就不能在BULK COLLECT INTO子句中使用。
- 如果有一个隐式的数据类型转换,复合目标的集合(如对象类型集合)就不能用于BULK COLLECTINTO子句中。
7. FORALL与BULK COLLECT综合运用
FORALL与BULK COLLECT是实现批量SQL的两个重要方式,我们可以将其结合使用以提高性能。
示例:
-- 创建表tb_emp
CREATE TABLE tb_emp AS
SELECT empno, ename, hiredate
FROM emp
WHERE 1 = 0; DECLARE
-- 声明游标
CURSOR emp_cur IS
SELECT empno, ename, hiredate FROM emp;
-- 基于游标的嵌套表类型
TYPE nested_emp_type IS TABLE OF emp_cur%ROWTYPE;
-- 声明变量
emp_tab nested_emp_type;
BEGIN
SELECT empno, ename, hiredate
BULK COLLECT INTO emp_tab
FROM emp
WHERE sal > 1000; -- 使用FORALL语句将变量中的数据插入到表tb_emp
FORALL i IN 1 .. emp_tab.COUNT
INSERT INTO (SELECT empno, ename, hiredate FROM tb_emp)
VALUES emp_tab( i ); COMMIT;
DBMS_OUTPUT.put_line('总共向 tb_emp 表中插入记录数: ' || emp_tab.COUNT);
END;
Oracle数据库之FORALL与BULK COLLECT语句的更多相关文章
- ORACLE批量绑定FORALL与BULK COLLECT
FORALL与BULK COLLECT的使用方法: 1.使用FORALL比FOR效率高,因为前者只切换一次上下文,而后者将是在循环次数一样多个上下文间切换. 2.使用BLUK COLLECT一次取出一 ...
- Oracle数据库查找持有锁的SQL语句,而不是请求锁的SQL语句(原创)
Oracle数据库查找持有锁的SQL语句,而不是请求锁的SQL语句 查找活动的事务以及活动事务关联的会话信息 select s.sid 会话ID, s.serial# 会话序列号, s.usernam ...
- ORACLE数据库 常用命令和Sql常用语句
ORACLE 账号相关 如何获取表及权限 1.COPY表空间backup scottexp登录管理员账号system2.创建用户 create user han identified(认证) by m ...
- 2015.1.15 利用Oracle函数插入表结构 Bulk collect into 不用循环,简洁高效
如果表结构只对应一个字段,可以 select col1 bulk collect into 变量,不用游标循环,简洁高效 create or replace function get_airway_s ...
- oracle数据库常用的99条查询语句(转载)
1. select * from emp; 2. select empno, ename, job from emp; 3. select empno 编号, ename 姓名, job 工作 fro ...
- oracle数据库常用的99条查询语句
1. select * from emp; 2. select empno, ename, job from emp; 3. select empno 编号, ename 姓名, job 工作 fro ...
- 查看oracle数据库最近执行了哪些sql语句
SELECT b.sql_text, --content of SQL a.machine, --which machine run this code a.username, a.module, - ...
- Oracle数据库列出所有表名SQL语句
select table_name from user_tables
- 吴裕雄--天生自然ORACLE数据库学习笔记:优化SQL语句
create or replace procedure trun_table(table_deleted in varchar2) as --创建一个存储过程,传入一个表示表名称的参数,实现清空指定的 ...
随机推荐
- PCB模擬設計接地的指導原則
接地無疑是系統設計中最為棘手的問題之一.盡管它的概念相對比較簡單,實施起來卻很復雜,遺憾的是,它沒有一個簡明扼要可以用詳細步驟描述的方法來保證取得良好效果,但如果在某些細節上處理不當,可能會導致令人頭 ...
- 替代 yunio , 推荐一个国外的云存储(同步盘) wuala(这个网站也挂了)——功能评测非常全面
替代 yunio , 推荐一个国外的云存储(同步盘) wuala 话说自从云诺开始收费后,我的文件便再没同步或备份过,前天没事便找了一下,发现一个国外的网盘,注册送 5 G 空间(对我来说同步日常文件 ...
- 双向队列 STL
题目描述 想想双向链表……双向队列的定义差不多,也就是说一个队列的队尾同时也是队首:两头都可以做出队,入队的操作.现在给你一系列的操作,请输出最后队列的状态:命令格式:LIN X X表示一个整数,命 ...
- COJ 1002 WZJ的数据结构(二)(splay模板)
我的LCC,LCT,Splay格式终于统一起来了... 另外..这个形式的Splay是标准的Splay(怎么鉴别呢?看Splay函数是否只传了一个变量node就行),刘汝佳小白书的Splay写的真是不 ...
- Hadoop InputFormat
Hadoop可以处理不同数据格式(数据源)的数据,从文本文件到(非)关系型数据库,这很大程度上得益于Hadoop InputFormat的可扩展性设计,InputFormat层次结构图如下:
- 解决nginx session共享的问题
不使用session,换作cookie 能把session改成cookie,就能避开session的一些弊端,在从前看的一本J2EE的书上,也指明在集群系统中不能用session,否则惹出祸端来就不好 ...
- OpenStack入门之初步认识
一.OpenStack 入门 之 基础知识 二.OpenStack 入门 之 基本组件 三.OpenStack 入门 之 各组件解析(基础) 四.OpenStack 入门 之 各组件解析(进阶) 五. ...
- java笔记12之面向对象初始
1 概述 类:是一组相关的属性和行为的集合.是一个抽象的概念. 对象:是该类事物的具体表现形式.具体存在的个体. (1)面向对象思想 面向对象是基于面向过程的编程思想. ...
- iOS APP之本地数据存储(译)
最近工作中完成了项目的用户信息本地存储,查阅了一些本地存储加密方法等相关资料.期间发现了一个来自印度理工学院(IIT)的信息安全工程师的个人博客,写了大量有关iOS Application secur ...
- Ionic文件目录说明
hooks //google之后这个目录应该是在编译cordova时自定义的脚本命令,方便整合到我们的编译系统和版本控制系统中 plugins //cordova插件的目录,插件的安装下一节详述 ...