PL/SQL批处理语句:BULK COLLECT 和 FORALL

   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;

 

 

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/535348.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

redis-full-check

https://github.com/alibaba/RedisFullCheck/releases redis-full-check是阿里云Redis&MongoDB团队开源的用于校验2个redis数据是否一致的工具。   redis-full-check通过全量对比源端和目的端的redis中的数据的方式来进行数据校验&#xff0c;其比较方式通过多轮次比较&a…

Docker目录挂载

Docker容器启动的时候&#xff0c;如果要挂载宿主机的一个目录&#xff0c;可以用-v参数指定。 譬如我要启动一个centos容器&#xff0c;宿主机的/test目录挂载到容器的/soft目录&#xff0c;可通过以下方式指定&#xff1a; # docker run -it -v /test:/soft centos /bin/ba…

Redis主从复制原理学习

Redis主从复制原理学习总结 - 运维笔记 和Mysql主从复制的原因一样&#xff0c;Redis虽然读取写入的速度都特别快&#xff0c;但是也会产生读压力特别大的情况。为了分担读压力&#xff0c;Redis支持主从复制&#xff0c;Redis的主从结构可以采用一主多从或者级联结构&#xff…

Redis哨兵模式(sentinel)学习总结及部署记录(主从复制、读写分离、主从切换)

Redis的集群方案大致有三种&#xff1a;1&#xff09;redis cluster集群方案&#xff1b;2&#xff09;master/slave主从方案&#xff1b;3&#xff09;哨兵模式来进行主从替换以及故障恢复。 一、sentinel哨兵模式介绍 Sentinel(哨兵)是用于监控redis集群中Master状态的工具&…

Redis之Redis内存模型

Redis是目前最火爆的内存数据库之一&#xff0c;通过在内存中读写数据&#xff0c;大大提高了读写速度&#xff0c;可以说Redis是实现网站高并发不可或缺的一部分。 我们使用Redis时&#xff0c;会接触Redis的5种对象类型&#xff08;字符串、哈希、列表、集合、有序集合&…

Redis监控指标

监控指标 •性能指标&#xff1a;Performance•内存指标: Memory•基本活动指标&#xff1a;Basic activity•持久性指标: Persistence•错误指标&#xff1a;Error 性能指标&#xff1a;Performance NameDescriptionlatencyRedis响应一个请求的时间instantaneous_ops_per_s…

MHA高可用

manager 组件 masterha_manger # 启动MHA masterha_check_ssh # 检查MHA的SSH配置状况 masterha_check_repl # 检查MySQL复制状况&#xff0c;配置信息 masterha_master_monitor # 检测master是否宕机 masterha_check_status # 检测当…

MySQL Replication需要注意的问题

主库意外宕机 如果没有设置主库的sync_binlog选项&#xff0c;就可能在奔溃前没有将最后的几个二进制日志事件刷新到磁盘中。备库I/O线程因此也可一直处于读不到尚未写入磁盘的事件的状态中。当主库从新启动时&#xff0c;备库将重连到主库并再次尝试去读该事件&#xff0c;但…

update和delete操作忘加where条件导致全表更新的处理方法

在数据库日常维护中&#xff0c;开发人员是最让人头痛的&#xff0c;很多时候都会由于SQL语句写的有问题导致服务器出问题&#xff0c;导致资源耗尽。最危险的操作就是在做DML操作的时候忘加where条件&#xff0c;导致全表更新&#xff0c;这是作为运维或者DBA的我们改如何处理…

Innodb结构

从MySQL5.5版本开始默认使用InnoDB作为引擎&#xff0c;它擅长处理事务&#xff0c;具有自动崩满恢复的特性&#xff0c;在日常开发中使用非常广泛&#xff0c;下面是言方的InnoDB引擎美构图&#xff0c;主要分为内存结构和磁盘结构两大部分。 内存结构主要包括Buffer Pool、C…

innodb_flush_method理解【转】

innodb_flush_method这个参数控制着innodb数据文件及redo log的打开、刷写模式&#xff0c;对于这个参数&#xff0c;文档上是这样描述的&#xff1a; 有三个值&#xff1a;fdatasync(默认)&#xff0c;O_DSYNC&#xff0c;O_DIRECT 默认是fdatasync&#xff0c;调用fsync()去…

Docker容器间网络通信

自从Docker容器出现以来&#xff0c;容器网络通信就一直是被关注的焦点&#xff0c;也是生产环境的迫切需求。容器网络通信又分为两大方面&#xff1a;单主机容器上的相互通信&#xff0c;和跨主机的容器相互通信。 一、Docker单主机容器通信 基于对net namespace的控制&#…

k8s二进制安装

1. 前言 之前文章安装 kubernetes 集群&#xff0c;都是使用 kubeadm 安装&#xff0c;然鹅很多公司也采用二进制方式搭建集群。这篇文章主要讲解&#xff0c;如何采用二进制包来搭建完整的高可用集群。相比使用 kubeadm 搭建&#xff0c;二进制搭建要繁琐很多&#xff0c;需要…

ELK学习笔记之Logstash详解

0x00 Logstash概述 官方介绍&#xff1a;Logstash is an open source data collection engine with real-time pipelining capabilities。简单来说logstash就是一根具备实时数据传输能力的管道&#xff0c;负责将数据信息从管道的输入端传输到管道的输出端&#xff1b;与此同时…

k8---proxy

kube-proxy 通过 Informer知道了Service、endpoints对象的创建&#xff0c;然后把service身上的CLUSTER-IP 和端口已经端点信息拿出来&#xff0c;创建iptable NAT规则做转发或通过ipvs模块创建VS服务器&#xff0c;这样经过CLUSTER-IP的流量都被转发到后端pod。 iptables模式 …

logstash密码设置步骤

生成密钥存储仓库 cd /app/logstash-6.8.4/bin/ ./logstash-keystore create 输入y 添加Logstash中可引用的变量 ./logstash-keystore add es_user 输入elastic ./logstash-keystore add es_pwd 输入bamBoo123 ./logstash-keystore add kafka_pwd 输入bamBoo123 ./logstash…

pycharm 自动补全括号 引号

过程&#xff1a; File->settings->Editior->General->Smart Keys 勾选Insert paired barckets(),[],{},<>

IndexError: list index out of range image_name=self.images_name[index]

这里是引用 def __len__(self):return len(self.images_name) 注意这个地方

Anaconda如何重新在开始菜单显示

步骤&#xff1a; &#xff08;1&#xff09; win R &#xff0c;然后输入Cmd 进入电脑命令端 &#xff08;2&#xff09;cd 进入anaconda 安装的路径&#xff0c;然后 输入 、、、 python .\Lib_nsis.py mkmenus 、、、 然后就OK了

python matplotlib 绘制曲线图,柱状图

matplotlib.pyplot.bar(x, y, width0.8, bottomNone, \*, aligncenter, dataNone, \*\*kwargs) x:x轴的数据值&#xff0c;或者标签 y:每个标签的值 width: 每个柱形图中每个柱子的宽度 bottom&#xff1a;y轴的基准值&#xff08;一般为0&#xff09; align: 柱形图中每个柱子…