本文原发布于简书,地址为: Oracle数据库之FORALL与BULK COLLECT语句。
更多数据库资讯请参看github: 数据库知识汇总
当PL/SQL运行时引擎处理一块代码时,它使用PL/SQL引擎来执行过程化的代码,而将SQL语句发送给SQL引擎来执行;
SQL引擎执行完毕后,将结果再返回给PL/SQL引擎。这种在PL/SQL引擎和SQL引擎之间的交互,称为上下文交换(context switch)。
每发生一次交换,就会带来必定的额外开销。git
这两个语句在PL/SQL内部进行一种数组处理,BULK COLLECT提供对数据的高速检索,FORALL可大大改进INSERT、UPDATE和DELETE操做的性能。Oracle数据库使用这些语句大大减小了PL/SQL与SQL语句执行引擎的环境切换次数,从而使其性能有了显著提升。github
若是你要插入5000条数据,通常状况下,在pl/sql中用for循环,循环插入5000次,而用forall一次就能够插入5000条,提升了性能和速度。
使用FORALL,能够将多个DML批量发送给SQL引擎来执行,最大限度地减小上下文交互所带来的开销。sql
FORALL index_name IN { lower_bound .. upper_bound | INDICES OF collection_name [ BETWEEN lower_bound AND upper_bound ] | VALUES OF index_collection } [ SAVE EXCEPTIONS ] dml_statement;
说明:数据库
见sqlscripts/forall-bulkcollect包下的sql脚本事例数组
使用FORALL时,应该遵循以下规则:服务器
--error statement --1.insert into test2 values dr_table(i);dbms_output.put_line(i);不正确,找不到i,由于forall中只能使用单条语句能够引用索引变量 --2.insert into test2 values(dr_table(i).id,dr_table(i).name);集合的field不能够在forall中使用,必须是总体使用 --3.insert into test2 values dr_table(i+1);错误,不能够对索引变量进行运算 --4.insert into test2 values(dr_table(i));报没有足够的值错误,此处外面不能够加括号,当有多个字段的时候,单个字段能够加括号
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一次便可提取全部行并绑定到记录变量,这就是所谓的批量绑定。网络
在游标中可使用BLUK COLLECT一次取出一个数据集合,比用游标单条取数据效率高,尤为是在网络不大好的状况下。oop
语法:性能
FETCH ... BULK COLLECT INTO ...[LIMIT row_number];
注意: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;
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;
FORALL与BULK COLLECT是实现批量SQL的两个重要方式,咱们能够将其结合使用以提升性能.
-- create tb_emp_test CREATE TABLE tb_emp_test AS SELECT empno, ename, hiredate FROM EMP_TEST WHERE 1 = 0; DECLARE -- declare cursor CURSOR EMP_CUR IS SELECT EMPNO, ENAME, HIREDATE FROM EMP_TEST; -- 基于游标的嵌套表类型 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_TEST WHERE SAL > 1000; -- 使用FORALL语句将变量中的数据插入到表tb_emp FORALL I IN 1 .. EMP_TAB.COUNT INSERT INTO (SELECT EMPNO, ENAME, HIREDATE FROM TB_EMP_TEST) VALUES EMP_TAB (I); COMMIT; DBMS_OUTPUT.PUT_LINE('总共向 tb_emp 表中插入记录数: ' || EMP_TAB.COUNT); END;
Oracle9i以前有binary_integer类型,和11g中引入的pls_integer数值范围相同:-2147483647~+2147483647,但pls_integer有更高的性能。二者性能均优于number类型。 Oracle中也引入了simple_integer类型,不过不能包含null值,范围:-2147483648~2147483647,性能优于pls_integer。
1. 加了"index by binary_integer "后,numbers类型的下标就是自增加,numbers类型在插入元素时,不须要初始化,不须要每次extend增长一个空间。 2. 若是没有这句话"index by binary_integer",那就得要显示对初始化,且每插入一个元素到numbers类型的table中时,都须要先extend。