本文主要翻译、整理了ORACLE官方文档上有关FORALL的部份内容,不妥之处,还希望多和大家交流。

在发送语句到SQL引擎前,FORALL语句告知PL/SQL 引擎批挷定输入集合。尽管FORALL语句包含一个迭代(iteration)模式,它并不一是个FOR循环。其语法为:

FORALL index IN lower_bound..upper_bound sql_statement;

一、如何使用批挷定提高性能(How Do Bulk Binds Improve Performance)

在PL/SQL 和SQL引擎(engines)中,太多的上下文切换(context switches)会影响性能。这个会发生在当一个循环为集合中的每个元素执行一个单个SQL语句时。而使用批挷定能显著提高性能。下图显示PL/SQL引擎 和SQL引擎之间的context switches:(PL/SQL引擎执行存过语句仅发送SQL语句到SQL引擎,SQL引擎执行语句后返回数据给PL/SQL引擎)

PL/SQL引擎发送一次SQL语句给SQL引擎,在SQL引擎中则为范围中每个index数字执行一次SQL语句。

PL/SQL挷定操作包含以下三类:

in-bind: When a PL/SQL variable or host variable is stored in the database by an INSERT or UPDATE statement.

out-bind:When a database value is assigned to a PL/SQL variable or a host variable by the RETURNING clause of an INSERT, UPDATE, or DELETE statement.

define: When a database value is assigned to a PL/SQL variable or a host variable by a SELECT or FETCH statement.

在SQL语句中,为PL/SQL变量指定值称为挷定(binding),

DML语句能传递所有集合元素到一个单个操作中,这过程称为批挷定(bulk binding)。

如果集合有20个元素,批挷定让你用单个操作等效于执行与20个SELECT,INSERT,UPDATE或DELETE语句。这个技术通过减少在PL/SQL和SQL引擎(engines)间的上下文切换来提高性能。批挷定包括:

1.带INSERT, UPDATE, and DELETE语句的批挷定:在FORALL语句中嵌入SQL语句

2.带SELECT语句的批挷定:在SELECT语句中用BULK COLLECT 语句代替INTO

下边的例子分别用FOR和FORALL进行数据插入,以显示用批挷定的对性能的提高:

SQL>SETSERVEROUTPUTONSQL>CREATETABLEparts (pnumNUMBER(4), pnameCHAR(15));

Tablecreated.

SQL>DECLARE2TYPE NumTabISTABLEOFparts.pnum%TYPEINDEXBYBINARY_INTEGER;

3TYPE NameTabISTABLEOFparts.pname%TYPEINDEXBYBINARY_INTEGER;

4pnums NumTab;

5Pnames NameTab;

6t1NUMBER;

7t2NUMBER;

8t3NUMBER;

9BEGIN10FORiIN1..500000LOOP

11pnums(i) :=i;

12pnames(i) :='Part No.'||to_char(i);

13ENDLOOP;

14t1 :=dbms_utility.get_time;

1516FORiIN1..500000LOOP

17INSERTINTOpartsVALUES(pnums(i),pnames(i));

18ENDLOOP;

19t2 :=dbms_utility.get_time;

2021FORALL iIN1..50000022INSERTINTOpartsVALUES(pnums(i),pnames(i));

23t3 :=dbms_utility.get_time;

2425dbms_output.put_line('Execution Time (secs)');

26dbms_output.put_line('---------------------');

27dbms_output.put_line('FOR loop:'||TO_CHAR(t2-t1));

28dbms_output.put_line('FORALL:'||TO_CHAR(t3-t2));

29END;

SQL>/Execution Time (secs)

---------------------FORloop:2592FORALL:358PL/SQLproceduresuccessfully completed

从而可以看出FORALL语句在性能上有显著提高。

注释:SQL语句能涉及多个集合,然而,性能提高只适用于下标集合(subscripted collections)

二、FORALL 如何影响回滚(How FORALL Affects Rollbacks)

在FORALL语句中,如果任何SQL语句执行产生未处理的异常(exception),先前执行的所有数据库改变都会被回滚。然而,如果产生的异常被捕获并处理,则回滚改变到一个隐式的保存点,该保存点在每个SQL语句执行前被标记。之前的改变不会被回滚。例如:

CREATETABLEemp2 (deptnoNUMBER(2), jobVARCHAR2(15));

INSERTINTOemp2VALUES(10,'Clerk');

INSERTINTOemp2VALUES(10,'Clerk');

INSERTINTOemp2VALUES(20,'Bookkeeper');--10-char job titleINSERTINTOemp2VALUES(30,'Analyst');

INSERTINTOemp2VALUES(30,'Analyst');

Comit;

DECLARETYPE NumListISTABLEOFNUMBER;

depts NumList :=NumList(10,20,30);

BEGINFORALL jINdepts.FIRST..depts.LAST

UPDATEemp2SETjob=job||'(temp)'WHEREdeptno=depts(j);

--raises a "value too large" exceptionEXCEPTION

WHENOTHERSTHENCOMMIT;

END;

/PL/SQLproceduresuccessfully completed

SQL>select*fromemp2;

DEPTNO JOB

---------- ---------------10Clerktemp10Clerktemp20Bookkeeper

30Analyst

30Analyst

上边的例子SQL引擎执行UPDATE语句3次,指定范围内的每个索引号一次。第一个(depts(10))执行成功,但是第二个(depts(20))执行失败(插入值超过了列长),因此,仅仅第二个执行被回滚。

当执行任何SQL语句引发异常时,FORALL语句中断(halt)。上边的例子中,执行第二个UPDATE语句引发异常, 因此第三个语句不会执行。

三、用%BULK_ROWCOUNT 属性计算FORALL迭代影响行数

在进行SQL数据操作语句时,SQL引擎打开一个隐式游标(命名为SQL),该游标的标量属性(scalar attribute)有 %FOUND, %ISOPEN, %NOTFOUND, and %ROWCOUNT。

FORALL语句除具有上边的标量属性外,还有个复合属性(composite attribute):%BULK_ROWCOUNT,该属性具有索引表(index-by table)语法。它的第i个元素存贮SQL语句(INSERT, UPDATE或DELETE)第i个执行的处理行数。如果第i个执行未影响行,%bulk_rowcount (i),返回0。FORALL与%bulk_rowcount属性使用相同下标。例如:

DECLARETYPE NumListISTABLEOFNUMBER;

depts NumList :=NumList(10,20,50);

BEGINFORALL jINdepts.FIRST..depts.LAST

UPDATEempSETsal=sal*1.10WHEREdeptno=depts(j);

--Did the 3rd UPDATE statement affect any rows?IFSQL%BULK_ROWCOUNT(3)=0THEN...

END;

%ROWCOUNT 返回SQL语句所有执行处理总的行数

%FOUND和 %NOTFOUND仅与SQL语句的最后执行有关,但是,可以使用%BULK_ROWCOUNT推断单个执行的值,如%BULK_ROWCOUNT(i)为0时,%FOUND和%NOTFOUND分别是FALSE和TRUE。

四、用%BULK_EXCEPTIONS属性处理FORALL异常

在执行FORALL语句期间,PL/SQL提供一个处理异常的机制。该机制使批挷定(bulk-bind)操作能保存异常信息并继续执行。方法是在FORALL语句中增加SAVE EXCEPTIONS关键字。语法为:

FORALL index IN lower_bound..upper_bound SAVE EXCEPTIONS

{insert_stmt | update_stmt | delete_stmt}

执行期间引发的所有异常都被保存游标属性 %BULK_EXCEPTIONS中,它存贮一个集合记录,每记录有两个字段:

%BULK_EXCEPTIONS(i).ERROR_INDEX:存贮在引发异常期间FORALL语句迭代(重复:iteration)

%BULK_EXCEPTIONS(i).ERROR_CODE:存贮相应的Oracle错误代码

%BULK_EXCEPTIONS.COUNT存贮异常的数量。(该属性不是%BULK_EXCEPTIONS集合记录的字段)。如果忽略SAVE EXCEPTIONS,当引发异常时,FORALL语句停止执行。此时,SQL%BULK_EXCEPTIONS.COUNT 返回1, 且SQL%BULK_EXCEPTIONS只包含一条记录。如果执行期间无异常 SQL%BULK_EXCEPTIONS.COUNT 返回 0.例子:

DECLARETYPE NumListISTABLEOFNUMBER;

num_tab NumList :=NumList(10,0,11,12,30,0,20,199,2,0,9,1);

errorsNUMBER;

dml_errors EXCEPTION;

PRAGMA exception_init(dml_errors,-24381);

BEGINFORALL iINnum_tab.FIRST..num_tab.LASTSAVEEXCEPTIONS

DELETEFROMempWHEREsal>500000/num_tab(i);

EXCEPTION

WHENdml_errorsTHENerrors :=SQL%BULK_EXCEPTIONS.COUNT;

dbms_output.put_line('Number of errors is'||errors);

FORiIN1..errors LOOP

dbms_output.put_line('Error'||i||'occurred during'||'iteration'||SQL%BULK_EXCEPTIONS(i).ERROR_INDEX);

dbms_output.put_line('Oracle error is'||SQLERRM(-SQL%BULK_EXCEPTIONS(i).ERROR_CODE));

ENDLOOP;

END;

该例子中,当i等于2,6,10时,产生异常ZERO_DIVIDE,完成后SQL%BULK_EXCEPTIONS.COUNT为3,其值为(2,1476), (6,1476)和(10,1476),错误输出如下:

Number of errors is 3

Error 1 occurred during iteration 2

Oracle error is ORA-01476: divisor is equal to zero

Error 2 occurred during iteration 6

Oracle error is ORA-01476: divisor is equal to zero

Error 3 occurred during iteration 10

Oracle error is ORA-01476: divisor is equal to zero

五、用BULK COLLECT子句取回查询结果至集合中

在返回到PL/SQL引擎之前,关键字BULK COLLECT告诉SQL引擎批挷定输出集合。该关键字能用于SELECT INTO, FETCH INTO和RETURNING INTO语句中。语法如下:

... BULK COLLECT INTO collection_name[, collection_name] ...

示例1:

DECLARETYPE NumTabISTABLEOFemp.empno%TYPE;

TYPE NameTabISTABLEOFemp.ename%TYPE;

enums NumTab;--no need to initializenames NameTab;

BEGINSELECTempno, enameBULKCOLLECTINTOenums, namesFROMemp;

...

END;

示例2:

CREATETYPE CoordsASOBJECT (xNUMBER, yNUMBER);

CREATETABLEgrid (numNUMBER, loc Coords);

INSERTINTOgridVALUES(10, Coords(1,2));

INSERTINTOgridVALUES(20, Coords(3,4));

DECLARETYPE CoordsTabISTABLEOFCoords;

pairs CoordsTab;

BEGINSELECTlocBULKCOLLECTINTOpairsFROMgrid;

--now pairs contains (1,2) and (3,4)END;

示例3:

DECLARETYPE SalListISTABLEOFemp.sal%TYPE;

sals SalList;

BEGINSELECTsalBULKCOLLECTINTOsalsFROMemp

WHEREROWNUM<=100;

...

END;

示例4:ExamplesofBulkFetchingfromaCursor:

DECLARETYPE NameListISTABLEOFemp.ename%TYPE;

TYPE SalListISTABLEOFemp.sal%TYPE;

CURSORc1ISSELECTename, salFROMempWHEREsal>1000;

names NameList;

sals SalList;

BEGINOPENc1;

FETCHc1BULKCOLLECTINTOnames, sals;--可返回到一个或多个集合END;

示例5:ExamplesofBulkFetchingfromaCursor:

DECLARETYPE DeptRecTabISTABLEOFdept%ROWTYPE;

dept_recs DeptRecTab;

CURSORc1ISSELECTdeptno, dname, locFROMdeptWHEREdeptno>10;

BEGINOPENc1;

FETCHc1BULKCOLLECTINTOdept_recs;--返回到一个记录(records)集合END;

forall oracle 游标,FORALL用法小结相关推荐

  1. oracle @spool,Oracle spool 用法小结

    Oracle spool 用法小结 转自:http://wallimn.javaeye.com/blog/472182 对于SPOOL 数据的SQL,最好要自己定义格式,以方便程序直接导入,SQL语句 ...

  2. oracle scur,详解Oracle游标的简易用法

    下面看下Oracle游标的简易用法,具体代码如下所示: create or replace procedure NW_DelYW(iOPERATION_ID number, sUserID varch ...

  3. [转载]Oracle 游标使用全解

    这个文档几乎包含了oracle游标使用的方方面面,全部通过了测试 -- 声明游标:CURSOR cursor_name IS select_statement --For 循环游标--(1)定义游标- ...

  4. Oracle 游标使用全解

    Oracle 游标使用全解 这个文档几乎包含了oracle游标使用的方方面面,全部通过了测试 -- 声明游标:CURSOR cursor_name IS select_statement --For ...

  5. 常见 Oracle HINT 的用法

    Hint 是Oracle 提供的一种SQL语法,它允许用户在SQL语句中插入相关的语法,从而影响SQL的执行方式. Oracle 19c HINT  Comments https://docs.ora ...

  6. oracle 游标while循环嵌套,oracle游标循环的嵌套

    完成批量修改user_tables中的所有表的栏位名(从MS SQL导入过来,发现大小写问题,造成很多麻烦) 存储过程见下: -- Created on 2012/3/14 by FREE decla ...

  7. oracle利用游标添加数据库,Oracle游标的使用实例详解

    什么是游标? ①从表中检索出结果集,从中每次指向一条记录进行交互的机制. ②关系数据库中的操作是在完整的行集合上执行的. 由 SELECT 语句返回的行集合包括满足该语句的 WHERE 子句所列条件的 ...

  8. 2.ORACLE游标的使用

    游标的概念 为了处理 SQL 语句,ORACLE 必须分配一片叫上下文( context area )的区域来处理所必需的信息,其中包括要处理的行的数目,一个指向语句被分析以后的表示形式的指针以及查询 ...

  9. ORACLE EXECUTE IMMEDIATE 用法

    先转载过来看,再整理吧~~~ ORACLE EXECUTE IMMEDIATE 用法 EXECUTE IMMEDIATE 代替了以前Oracle8i中DBMS_SQL package包. 它解析并马上 ...

最新文章

  1. 阿里巴巴Web前端面试的一道JS题目,求解答!!!
  2. android逆向分析概述_Android存储概述
  3. Confluence 6 查看内容索引概要
  4. strcpy函数的使用
  5. C++中如何定义某个数组的引用?
  6. php子类选择器代码,php – 可变产品选择器:获取实时选定值
  7. 美团搜索推荐多业务商品排序探索与实践
  8. AutoCAD VBA创建椭圆和样条曲线
  9. 如何用常量代替session_如何用Python代替Visual Basic应用程序并节省了很多时间
  10. makefile中的wildcard和notdir和patsubst
  11. vba判断文件编码格式_VBA编写Ribbon Custom UI编辑器07——写入xml
  12. Kconfig使用介绍
  13. Word弹窗提示“拼写或语法错误太多,无法继续显示”的处理办法
  14. 深度多模态子空间聚类网络+代码实现
  15. 40本编程开发电子书免费送
  16. 放大电路中的反馈(一)
  17. 解决js脚本加载失败的问题
  18. 2018年举办区块链峰会_2015年开放硬件峰会开放接受注册,征求建议
  19. 高校学生使用计算机软件,高校计算机运用软件教学
  20. 修改数据库表结构(SQLserver)

热门文章

  1. 关于qt学习的一点小记录(1)
  2. Maven 学习之旅
  3. linux 分区下的两主要硬盘的意思
  4. 如何恢复默认域策略和默认域控制器策略
  5. JAVA从下载到浏览器运行完整篇,写给lewis
  6. TikTok游戏话题浏览上涨,预示出海新机会
  7. dedecms arclist中截取字符串的方法
  8. vue中的mixins怎么用?
  9. node.js 调试 eggs launch.json配置信息
  10. fresco的使用教程