forall oracle 游标,FORALL用法小结
本文主要翻译、整理了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用法小结相关推荐
- oracle @spool,Oracle spool 用法小结
Oracle spool 用法小结 转自:http://wallimn.javaeye.com/blog/472182 对于SPOOL 数据的SQL,最好要自己定义格式,以方便程序直接导入,SQL语句 ...
- oracle scur,详解Oracle游标的简易用法
下面看下Oracle游标的简易用法,具体代码如下所示: create or replace procedure NW_DelYW(iOPERATION_ID number, sUserID varch ...
- [转载]Oracle 游标使用全解
这个文档几乎包含了oracle游标使用的方方面面,全部通过了测试 -- 声明游标:CURSOR cursor_name IS select_statement --For 循环游标--(1)定义游标- ...
- Oracle 游标使用全解
Oracle 游标使用全解 这个文档几乎包含了oracle游标使用的方方面面,全部通过了测试 -- 声明游标:CURSOR cursor_name IS select_statement --For ...
- 常见 Oracle HINT 的用法
Hint 是Oracle 提供的一种SQL语法,它允许用户在SQL语句中插入相关的语法,从而影响SQL的执行方式. Oracle 19c HINT Comments https://docs.ora ...
- oracle 游标while循环嵌套,oracle游标循环的嵌套
完成批量修改user_tables中的所有表的栏位名(从MS SQL导入过来,发现大小写问题,造成很多麻烦) 存储过程见下: -- Created on 2012/3/14 by FREE decla ...
- oracle利用游标添加数据库,Oracle游标的使用实例详解
什么是游标? ①从表中检索出结果集,从中每次指向一条记录进行交互的机制. ②关系数据库中的操作是在完整的行集合上执行的. 由 SELECT 语句返回的行集合包括满足该语句的 WHERE 子句所列条件的 ...
- 2.ORACLE游标的使用
游标的概念 为了处理 SQL 语句,ORACLE 必须分配一片叫上下文( context area )的区域来处理所必需的信息,其中包括要处理的行的数目,一个指向语句被分析以后的表示形式的指针以及查询 ...
- ORACLE EXECUTE IMMEDIATE 用法
先转载过来看,再整理吧~~~ ORACLE EXECUTE IMMEDIATE 用法 EXECUTE IMMEDIATE 代替了以前Oracle8i中DBMS_SQL package包. 它解析并马上 ...
最新文章
- 阿里巴巴Web前端面试的一道JS题目,求解答!!!
- android逆向分析概述_Android存储概述
- Confluence 6 查看内容索引概要
- strcpy函数的使用
- C++中如何定义某个数组的引用?
- php子类选择器代码,php – 可变产品选择器:获取实时选定值
- 美团搜索推荐多业务商品排序探索与实践
- AutoCAD VBA创建椭圆和样条曲线
- 如何用常量代替session_如何用Python代替Visual Basic应用程序并节省了很多时间
- makefile中的wildcard和notdir和patsubst
- vba判断文件编码格式_VBA编写Ribbon Custom UI编辑器07——写入xml
- Kconfig使用介绍
- Word弹窗提示“拼写或语法错误太多,无法继续显示”的处理办法
- 深度多模态子空间聚类网络+代码实现
- 40本编程开发电子书免费送
- 放大电路中的反馈(一)
- 解决js脚本加载失败的问题
- 2018年举办区块链峰会_2015年开放硬件峰会开放接受注册,征求建议
- 高校学生使用计算机软件,高校计算机运用软件教学
- 修改数据库表结构(SQLserver)