PL/SQL程序中运行SQL语句是存在开销的,因为SQL语句是要提交给SQL引擎处理,这种在PL/SQL引擎和SQL引擎之间的控制转移叫做上下文却换,每次却换时,都有额外的开销
       请看下图:

但是,FORALL和BULK COLLECT可以让PL/SQL引擎把多个上下文却换压缩成一个,这使得在PL/SQL中的要处理多行记录的SQL语句执行的花费时间骤降
       请再看下图:

BULK COLLECT 加速查询

采用BULK COLLECT可以将查询结果一次性地加载到collections中,而不是通过cursor一条一条地处理
 可以在select into ,fetch into , returning into语句使用BULK COLLECT
 注意在使用BULK COLLECT时,所有的INTO变量都必须是collections

select into语句中使用bulk collect

DECLARE TYPE sallist IS TABLE OF employees.salary%TYPE;sals sallist;
BEGINSELECT salary BULK COLLECT INTO sals FROM employees where rownum<=50;--接下来使用集合中的数据
END;
/

在fetch into中使用bulk collect

DECLARETYPE deptrectab IS TABLE OF departments%ROWTYPE;dept_recs deptrectab;CURSOR cur IS SELECT department_id,department_name FROM departments where department_id>10;
BEGINOPEN cur;FETCH cur BULK COLLECT INTO dept_recs;--接下来使用集合中的数据
END;
/

returning into中使用bulk collect

CREATE TABLE emp AS SELECT * FROM employees;DECLARE TYPE numlist IS TABLE OF employees.employee_id%TYPE;enums numlist;TYPE namelist IS TABLE OF employees.last_name%TYPE;names namelist;
BEGINDELETE emp WHERE department_id=30RETURNING employee_id,last_name BULK COLLECT INTO enums,names;DBMS_OUTPUT.PUT_LINE('deleted'||SQL%ROWCOUNT||'rows:');FOR i IN enums.FIRST .. enums.LASTLOOPDBMS_OUTPUT.PUT_LINE('employee#'||enums(i)||':'||names(i));END LOOP;
END;
/deleted6rows:
employee#114:Raphaely
employee#115:Khoo
employee#116:Baida
employee#117:Tobias
employee#118:Himuro
employee#119:Colmenares

BULK COLLECT 对大数据DELETE UPDATE的优化

DECLARE
--按rowid排序的cursor
--删除条件是oo=xx,这个需根据实际情况来定CURSOR mycursor IS SELECT rowid FROM t WHERE OO=XX ORDER BY rowid;TYPE rowid_table_type IS TABLE OF rowid index  by  pls_integer;v_rowid rowid_table_type;
BEGINOPEN mycursor;LOOPFETCH mycursor BULK COLLECT INTO v_rowid LIMIT 5000;--5000行提交一次EXIT WHEN v_rowid.count=0;FORALL i IN v_rowid.FIRST..v_rowid.LASTDELETE t WHERE rowid=v_rowid(i);COMMIT;END LOOP;CLOSE mycursor;
END;
/

限制BULK COLLECT 提取的记录数

语法:
             FETCH cursor BULK COLLECT INTO ...[LIMIT rows];
             其中,rows可以是常量,变量或者求值的结果是整数的表达式
             
             假设你需要查询并处理1W行数据,你可以用BULK COLLECT一次取出所有行,然后填充到一个非常大的集合中
             可是,这种方法会消耗该会话的大量PGA,APP可能会因为PGA换页而导致性能下降
             
             这时,LIMIT子句就非常有用,它可以帮助我们控制程序用多大内存来处理数据

DECLARECURSOR allrows_cur IS SELECT * FROM employees;TYPE employee_aat IS TABLE OF allrows_cur%ROWTYPE INDEX BY BINARY_INTEGER;v_emp employee_aat;
BEGINOPEN allrows_cur;LOOPFETCH allrows_cur BULK FETCH INTO v_emp LIMIT 100;/*通过扫描集合对数据进行处理*/FOR i IN 1 .. v_emp.countLOOPupgrade_employee_status(v_emp(i).employee_id);END LOOP;EXIT WHEN allrows_cur%NOTFOUND;END LOOP;CLOSE allrows_cur;
END;
/

 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中使用的集合,下标不能使用表达式。

BULK COLLECT介绍

BULK COLLECT子句会批量检索结果,即一次性将结果集绑定到一个集合变量中,并从SQL引擎发送到PL/SQL引擎。

通常可以在SELECT INTO、FETCH INTO以及RETURNING INTO子句中使用BULK COLLECT。下面逐一描述BULK COLLECT在这几种情形下的用法

BULK COLLECT的注意事项

  1. BULK COLLECT INTO 的目标对象必须是集合类型。
  2. 只能在服务器端的程序中使用BULK COLLECT,如果在客户端使用,就会产生一个不支持这个特性的错误。
  3. 不能对使用字符串类型作键的关联数组使用BULK COLLECT子句。
  4. 复合目标(如对象类型)不能在RETURNING INTO子句中使用。
  5. 如果有多个隐式的数据类型转换的情况存在,多重复合目标就不能在BULK COLLECT INTO子句中使用。
  6. 如果有一个隐式的数据类型转换,复合目标的集合(如对象类型集合)就不能用于BULK COLLECTINTO子句中
 DECLARE
CURSOR cur IS
select * from NEWLOG4 t where TO_CHAR(t.autudt,'YYYY-MM-DD') = '2021-03-09'and pushstate='3';
TYPE rec IS TABLE OF NEWLOG4%ROWTYPE;
recs rec;
BEGIN
OPEN cur;
WHILE (TRUE) LOOP
FETCH cur BULK COLLECT
INTO recs LIMIT 5000;
FORALL i IN 1 .. recs.COUNT
INSERT INTO NEWLOG4_202103 VALUES recs (i);COMMIT;EXIT WHEN cur%NOTFOUND;END LOOP;CLOSE cur;END;INSERT/*+parallel(10)*/  INTO NEWLOG4_202103 select * from NEWLOG4 t where TO_CHAR(t.autudt,'YYYY-MM-DD') = '2021-03-09'and pushstate='3';delete /*+parallel(10)*/  from NEWLOG4  nologging  where TO_CHAR(autudt,'YYYY-MM-DD') = '2021-03-09'and pushstate='3';INSERT/*+parallel(10)*/  INTO NEWLOG4_202103 select * from NEWLOG4 t where TO_CHAR(t.autudt,'YYYY-MM-DD') = '2021-03-09'and pushstate='3';alter session enable parallel dml; DECLARE
CURSOR cur IS
select/*+parallel(8)*/ rowid from NEWLOG4 t where TO_CHAR(t.autudt,'YYYY-MM-DD') = '2021-03-03'and pushstate='3';
--TYPE rec IS TABLE OF NEWLOG4%ROWTYPE;
TYPE rowid_table_type IS TABLE OF rowid index  by  pls_integer;v_rowid rowid_table_type;
--recs rec;
BEGIN
OPEN cur;
WHILE (TRUE) LOOP
FETCH cur BULK COLLECT
INTO v_rowid  LIMIT 1000;
EXIT WHEN v_rowid.count=0;
FORALL i IN 1 .. v_rowid.COUNT
---delete NEWLOG4 where current of recs (i);
delete/*+parallel(8)*/ from NEWLOG4 nologging where rowid=v_rowid (i);COMMIT;EXIT WHEN cur%NOTFOUND;END LOOP;CLOSE cur;END;DECLARE
v_exists  NUMBER (10, 0);
v_exists1  NUMBER (10, 0);
--recs rec;
BEGIN
select /*+parallel(12)*/ count(1)into v_exists from NEWLOG4 t where TO_CHAR(t.autudt,'YYYY-MM-dd') = '2021-03-06'and pushstate='3';
WHILE (TRUE) LOOPdelete /*+parallel(12)*/  from NEWLOG4  nologging  where TO_CHAR(autudt,'YYYY-MM-DD') = '2021-03-06'and pushstate='3';EXIT WHEN v_exists1=v_exists+1;v_exists1:= v_exists1+1;if (v_exists=10000) thenCOMMIT;end if;END LOOP;COMMIT;END;select count(1) from NEWLOG4 t where TO_CHAR(t.autudt,'YYYY-MM-DD') = '2021-03-01'

select b.sid, b.username, b.serial#, a.spid, b.paddr, c.sql_text, b.machinefrom v$process a, v$session b, v$sqlarea cwhere a.addr = b.paddrand b.sql_hash_value = c.hash_value;
CREATE OR REPLACE PROCEDURE NEWLOG4_SUB_TABLE2 (delete_date in varchar2)
IS-- table_name1              VARCHAR2(50);-- create_table_sql         VARCHAR2(4000);
--  insert_data_sql      VARCHAR2(4000);delete_data_sql      VARCHAR2(4000);
--  v_exists INT:=0;--  v_exists  NUMBER (10, 0);CURSOR cur IS
select * from NEWLOG4 t where TO_CHAR(t.autudt,'YYYY-MM-DD') = delete_date and pushstate='3';
TYPE rec IS TABLE OF NEWLOG4%ROWTYPE;
recs rec;
BEGIN--将FATHER_TABLE表中取上月记录 添加到新创建的分表中。-- insert_data_sql := 'INSERT INTO ' || table_name1 || ' SELECT * FROM NEWLOG4 WHERE autudt <(systimestamp - NUMTODSINTERVAL(30,''day'')) and pushstate=''3''';-- EXECUTE IMMEDIATE insert_data_sql;
OPEN cur;
WHILE (TRUE) LOOP
FETCH cur BULK COLLECT
INTO recs LIMIT 5000;
FORALL i IN 1 .. recs.COUNT
INSERT INTO NEWLOG4_202103_10 VALUES recs (i);COMMIT;EXIT WHEN cur%NOTFOUND;END LOOP;CLOSE cur;--删除FATHER_TABLE表中时间在上个月范围内的所有数据--delete_data_sql := 'DELETE FROM NEWLOG4 WHERE autudt <(systimestamp-NUMTODSINTERVAL(30,''day''))and pushstate=''3''';delete_data_sql :='delete /*+parallel(10)*/  from NEWLOG4  nologging  where TO_CHAR(t.autudt,''YYYY-MM-DD'') = delete_date and pushstate=''3''';EXECUTE IMMEDIATE delete_data_sql;COMMIT;--EXCEPTION--WHEN OTHERS THEN--   ROLLBACK;
END NEWLOG4_SUB_TABLE2;
CREATE OR REPLACE PROCEDURE NEWLOG4_SUB_TABLE
IStable_name1              VARCHAR2(50);create_table_sql         VARCHAR2(4000);insert_data_sql      VARCHAR2(4000);delete_data_sql      VARCHAR2(4000);
--  v_exists INT:=0;v_exists  NUMBER (10, 0);
BEGINSELECT 'NEWLOG4_' || TO_CHAR(ADD_MONTHS(SYSDATE, -1), 'YYYYMM') INTO table_name1 FROM DUAL;select count(1) into v_exists from user_tables where table_name=UPPER(table_name1);--dbms_output.put_line(sname);--dbms_output.put_line(table_name1);if (v_exists <1)then-- dbms_output.put_line(sname);create_table_sql := 'create table ' || table_name1 || ' (autudt          TIMESTAMP(6),authentype      VARCHAR2(2000),userid          VARCHAR2(2000),orgid           VARCHAR2(2000),org2id          VARCHAR2(2000),realname        VARCHAR2(2000),success         VARCHAR2(2000),idpname         VARCHAR2(2000),idpip           VARCHAR2(2000),vistorip        VARCHAR2(2000),vistorbrowser   VARCHAR2(2000),spid            VARCHAR2(2000),spurl           VARCHAR2(2000),info            VARCHAR2(2000),autdesc         VARCHAR2(2000),taketime        VARCHAR2(2000),orgnamefullpath VARCHAR2(2000),ines            INTEGER default 0,logid           VARCHAR2(32),inputaccount    VARCHAR2(2000),channel         NUMBER(32),pushstate       NUMBER(2) default 0,appid           VARCHAR2(50))';EXECUTE IMMEDIATE create_table_sql;commit;end if;--将FATHER_TABLE表中取上月记录 添加到新创建的分表中。insert_data_sql := 'INSERT INTO ' || table_name1 || ' SELECT * FROM NEWLOG4 WHERE autudt <(systimestamp - NUMTODSINTERVAL(30,''day'')) and pushstate=''3''';EXECUTE IMMEDIATE insert_data_sql;--删除FATHER_TABLE表中时间在上个月范围内的所有数据delete_data_sql := 'DELETE FROM NEWLOG4 WHERE autudt <(systimestamp-NUMTODSINTERVAL(30,''day''))and pushstate=''3''';EXECUTE IMMEDIATE delete_data_sql;COMMIT;--EXCEPTION--WHEN OTHERS THEN--   ROLLBACK;
END NEWLOG4_SUB_TABLE;
CREATE OR REPLACE PROCEDURE NEWLOG4_day_TABLE (delete_date in varchar2)
ISinsert_data_sql      VARCHAR2(4000);delete_data_sql      VARCHAR2(4000);CURSOR cur IS
select * from NEWLOG4 t where TO_CHAR(t.autudt,'YYYY-MM-DD') = delete_date and pushstate='3';
TYPE rec IS TABLE OF NEWLOG4%ROWTYPE;
recs rec;
BEGIN
OPEN cur;
WHILE (TRUE) LOOP
FETCH cur BULK COLLECT
INTO recs LIMIT 5000;
FORALL i IN 1 .. recs.COUNT
--EXECUTE IMMEDIATE insert_data_sql;
INSERT INTO NEWLOG4_DAY_INTERVAL_PARTITION VALUES recs (i);COMMIT;EXIT WHEN cur%NOTFOUND;END LOOP;CLOSE cur;--EXCEPTION--WHEN OTHERS THEN--   ROLLBACK;
END NEWLOG4_day_TABLE;

PL/SQL批处理语句:BULK COLLECT 和 FORALL相关推荐

  1. pl/sql插入语句插入数据库中文为????

    本地安装完数据库和PL/SQL客户端后,执行insert语句后中文部分显示????乱码. 个人执行select userenv('language') from dual;  结果为: America ...

  2. Oracle笔记 六、PL/SQL简单语句块、变量定义

    1.简单SQL语句,HellWorld示例 --输出信息 begin dbms_output.put_line('Oracle Hello World!'); end; 2.变量的定义.使用 --定义 ...

  3. [翻译]自动维护索引重新生成组织的SQL批处理语句

    脚本来自<Inside Server 2005 T-SQL Programming> SET NOCOUNT ON;DECLARE @objectid int;DECLARE @index ...

  4. oracle 优化之批量处理bulk correct 和 forall

    世风之狡诈多端,到底忠厚人颠扑不破: 末俗以繁华相尚,终觉冷淡处趣味弥长. BULK   COLLECT运用 在游标中运用 declare cursor C_CUR is SELECT * FROM  ...

  5. PostgreSQL Oracle 兼容性之 - PL/SQL FORALL, BULK COLLECT

    Oracle PL/SQL 开发的童鞋,一定对O家的bulk批量处理的性能很是赞赏吧. 但是PostgreSQL用户请不要垂涎,作为学院派和工业界的一颗璀璨明珠. 开源数据库PostgreSQL,也有 ...

  6. 金仓数据库KingbaseES数据库开发指南(4. 面向应用程序的PL/SQL开发)

    目录 4.1. PL/SQL子程序和包的编写 ¶ 4.1.1. PL/SQL 子程序概述 ¶ 4.1.2. PL/SQL 包概述 ¶ 4.1.3. PL/SQL 单元概述 ¶ 4.1.4. 创建 PL ...

  7. bulk怎么使用oracle,oracle学习之bulk collect用法

    通过bulk collect减少loop处理的开销,使用Bulk Collect提高Oracle查询效率 Oracle8i中首次引入了Bulk Collect特性,该特性可以让我们在PL/SQL中能使 ...

  8. PL/SQL 记录与集合

    一.记录类型简介 不使用记录的PLSQL语句块 declare v_empno  NUMBER; v_ename  varchar2(20); v_job varchar2(9); v_mgr NUM ...

  9. oracle bulk collect into,BULK COLLECT INTO

    下面我们来看两个范例,一个是使用BULK COLLECT的,一个没有: 范例1.使用多次循环来检索并显示数据. 范例2.一次性获取数据,然后再循环显示. 下面是一些有关BULK COLLECT的建议: ...

最新文章

  1. 为物联网产业化发展提供坚实保障
  2. ubuntu部署java环境
  3. MP3Player(附源码)
  4. GIT学习笔记二(本地项目发布到GIT)
  5. 无表头单链表的总结----删除节点
  6. 【图文详解】在电脑中打开.properties配置文件时会生成一个空的名为Java的文件问题
  7. react性能优化方案_React灵敏且性能卓越的Spray + Akka解决方案,以“在Java和Node.js中发挥并发性和性能”...
  8. P8级别的顶级“并发编程”宝典,最全指南
  9. Windows平台RTMP播放器/RTSP播放器如何在播放窗口添加OSD文字叠加
  10. 如何将DataGrid中任意行背景色设置为#00A000?
  11. 2018青岛ICPC ZOJ 4061: Magic Multiplication(模拟)
  12. c语言编程发展史详细介绍,一张图让你了解编程语言发展史
  13. 20200717每日一句
  14. OTA时代来了!由新一代私有云揭开序幕
  15. 如何搭建企业大数据分析平台
  16. python龙卷风框架,龙卷风python的简单异步示例
  17. 推荐一个国学资源分享、学术交流网站:龙腾网
  18. Multiclass Weighted Loss for Instance Segmentation of Cluttered Cells
  19. 工作站属于通用计算机吗,我们常说的工作站到底和普通电脑有什么不一样?工作站好在哪?...
  20. 图像处理的数学模型与高性能算法——介绍

热门文章

  1. python数字字符串乘以2_Python基础(2)_数字和字符串类型
  2. windows如何生成ssh密钥
  3. qt设置行编辑器不可编辑
  4. qt先生成json文件后程序启动时读取json文件在一组数据模拟下正常,换一组数据就出现乱码
  5. android 5. 蓝牙 mesh,蓝牙mesh组网
  6. java 数据队列_Java 数据结构 - 队列
  7. java 代理ip工具类_Java基础之java处理ip的工具类
  8. mysql 共享表空间存储_MySQL InnoDB共享表空间
  9. github流程图_逆天插件,VSCode里也能画流程图了?Visio可以淘汰了?
  10. linux本地mysql与服务器同步数据_linux下指定mysql数据库服务器主从同步的配置实例...