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引擎之间的多次上下文交换压缩为一次交换:

  1. FORALL,用于增强PL/SQL引擎到SQL引擎的交换。
  2. 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时,应该遵循如下规则:

  1. FORALL语句的执行体,必须是一个单独的DML语句,比如INSERT,UPDATE或DELETE。
  2. 不要显式定义index_row,它被PL/SQL引擎隐式定义为PLS_INTEGER类型,并且它的作用域也仅仅是FORALL。
  3. 这个DML语句必须与一个集合的元素相关,并且使用FORALL中的index_row来索引。注意不要因为index_row导致集合下标越界。
  4. lower_bound和upper_bound之间是按照步进 1 来递增的。
  5. 在sql_statement中,不能单独地引用集合中的元素,只能批量地使用集合。
  6. 在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的注意事项

  1. BULK COLLECT INTO 的目标对象必须是集合类型。
  2. 只能在服务器端的程序中使用BULK COLLECT,如果在客户端使用,就会产生一个不支持这个特性的错误。
  3. 不能对使用字符串类型作键的关联数组使用BULK COLLECT子句。
  4. 复合目标(如对象类型)不能在RETURNING INTO子句中使用。
  5. 如果有多个隐式的数据类型转换的情况存在,多重复合目标就不能在BULK COLLECT INTO子句中使用。
  6. 如果有一个隐式的数据类型转换,复合目标的集合(如对象类型集合)就不能用于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语句的更多相关文章

  1. ORACLE批量绑定FORALL与BULK COLLECT

    FORALL与BULK COLLECT的使用方法: 1.使用FORALL比FOR效率高,因为前者只切换一次上下文,而后者将是在循环次数一样多个上下文间切换. 2.使用BLUK COLLECT一次取出一 ...

  2. Oracle数据库查找持有锁的SQL语句,而不是请求锁的SQL语句(原创)

    Oracle数据库查找持有锁的SQL语句,而不是请求锁的SQL语句 查找活动的事务以及活动事务关联的会话信息 select s.sid 会话ID, s.serial# 会话序列号, s.usernam ...

  3. ORACLE数据库 常用命令和Sql常用语句

    ORACLE 账号相关 如何获取表及权限 1.COPY表空间backup scottexp登录管理员账号system2.创建用户 create user han identified(认证) by m ...

  4. 2015.1.15 利用Oracle函数插入表结构 Bulk collect into 不用循环,简洁高效

    如果表结构只对应一个字段,可以 select col1 bulk collect into 变量,不用游标循环,简洁高效 create or replace function get_airway_s ...

  5. oracle数据库常用的99条查询语句(转载)

    1. select * from emp; 2. select empno, ename, job from emp; 3. select empno 编号, ename 姓名, job 工作 fro ...

  6. oracle数据库常用的99条查询语句

    1. select * from emp; 2. select empno, ename, job from emp; 3. select empno 编号, ename 姓名, job 工作 fro ...

  7. 查看oracle数据库最近执行了哪些sql语句

    SELECT b.sql_text, --content of SQL a.machine, --which machine run this code a.username, a.module, - ...

  8. Oracle数据库列出所有表名SQL语句

    select table_name from user_tables

  9. 吴裕雄--天生自然ORACLE数据库学习笔记:优化SQL语句

    create or replace procedure trun_table(table_deleted in varchar2) as --创建一个存储过程,传入一个表示表名称的参数,实现清空指定的 ...

随机推荐

  1. 【JavsScript】关于javascript的路线

    Client JS: Level 1 基本对象的掌握----------------------------------------->Library(兼容)/Widget(UI+功能)---- ...

  2. ATR的基本结构与意义(无历史字符部分)

    Reset 3B FA 13 00 00 81 31 FE 45 4A 43 4F 50 34 31 56 32 32 31 96 复位应答 ATR TS( The Initial character ...

  3. 运行出现 Multiple dex files define Landroid/support/annotation/AnimRes 解决方法

    1. 拷贝google-play-services_lib/libs/目录下的android-support-v4.jar到xxxr/libs/下 2. 工程进行如下设置:

  4. 针对苹果最新审核要求:应用兼容IPv6

    在WWDC2015上苹果宣布iOS9将支持纯IPv6的网络服务.2016年初开始所有提交到App Store的应用必须支持IPv6.为确保现有的应用是兼容的,我们需要注意下面几点. 不建议使用底层的网 ...

  5. linux/hpux 添加用户

    #添加用户组, 组名dev, 组id为1001groupadd -g 1001 dev #添加用户组dev,不指定组idgroupadd dev #添加用户user1, id为111, 属于dev组, ...

  6. 对openflow 1.0协议的扩展

    通过这几天对openvswitch代码的分析,以及项目的须要,须要对openflow 1.0进行一定的扩展,发现网上没有这方面的教程,尽管在搞懂ovs代码架构,floodlight controlle ...

  7. android performClick使用

    performClick 是使用代码主动去调用控件的点击事件(模拟人手去触摸控件) ----------------------------------------- boolean android. ...

  8. ios开发应用内实现多语言自由切换

    需求描述:应用内部有一按钮,点击切换语言(如中英文切换).说起来这个是好久以前做的一个功能点了,刚开始也是没有头绪,后来解决了发现很简单,把方法分享一下.1.原理.查看NSLocalizedStrin ...

  9. yii图片上传

    http://wuhai.blog.51cto.com/2023916/953300 首先感谢这里的博主,提供了思路,不过在调用 $model->b_image->extensionNam ...

  10. yii [error] [exception.CHttpException.404] exception 'CHttpException' with message 'Unable to resolve the request "favicon.ico".'

    yii使用中,发现runtime文件夹下出现这个错误信息 解决办法:在生成的APP程序根目录下建.htaccess文件(前提是需要开启apache重写,具体如何开,查资料咯) 然后配置如下 <I ...