plsql bulk collect 详解

1 概述

1. 作用:提高 'sql 引擎'sql 语句执行情况返回给 'pl/sql 引擎' 的效率
   (1) bulk collect 会 '一次性' 将结果集绑定到一个 '集合变量',
       并从 sql 引擎发送到 pl/sql 引擎,大大减少 "上下文切换"
   
2. "上下文切换": 
   (1) 在 pl/sql 中,'pl/sql 块''pl/sql 引擎' 处理
   (2) 而其中的 'sql 语句' 则由 'pl/sql 引擎' 发送至 'sql 引擎处理' 
   (3) 后者处理完毕后再向前者返回数据,两者之间的通信称为 "上下文切换",
       过多的上下文切换将带来过量的性能负载。

3. pl/sqlsql 引擎之间的 交互("上下文切换"(1) bulk collect: 用于增强 'sql 引擎' -> 'pl/sql 引擎' 的交互
   (2) forall      : 用于增强 'pl/sql 引擎' -> 'sql 引擎' 的交互

扩展:plsql forall 详解

1.1 图示

在这里插入图片描述

2 三种使用方式

1. bulk collect 三种使用方式
   (1) select into   : 常规用法
   (2) fetch into    :和游标一起使用
   (3) returning into:和 insertupdatedelete 一起使用  

2. 注意
   (1) bulk collect into 后面一定是个 table 类型

基础数据准备:

create table stu_info (
  sno    number(3),
  sname  varchar2(30),
  sex   varchar(2)
);

insert into stu_info(sno, sname, sex) values(1, '瑶瑶', '女');
insert into stu_info(sno, sname, sex) values(2, '优优', '男');
insert into stu_info(sno, sname, sex) values(3, '倩倩', '女');
commit;

2.1 在 select into 中

declare
  type stu_info_table is table of scott.stu_info%rowtype;
  v_stu_info_rows stu_info_table;
begin
  -- 将结果集 一次性绑定 到记录记录变量中
  select si.sno, si.sname, si.sex
    bulk collect
    into v_stu_info_rows
    from stu_info si;

  -- 输出验证
  for i in v_stu_info_rows.first .. v_stu_info_rows.last loop
    dbms_output.put_line('sno: ' || v_stu_info_rows(i).sno || ', sname: ' || v_stu_info_rows(i)
                         .sname || ', sex: ' || v_stu_info_rows(i).sex);
  end loop;
end;

输出结果:

sno: 1, sname: 瑶瑶, sex: 女
sno: 2, sname: 优优, sex: 男
sno: 3, sname: 倩倩, sex: 女

2.2 在 fetch into 中

DECLARE
  TYPE stu_info_table IS TABLE OF scott.stu_info%ROWTYPE;
  v_stu_info_rows stu_info_table;

  CURSOR cur_stu_info IS
    SELECT si.sno, si.sname, si.sex FROM stu_info si;
BEGIN
  OPEN cur_stu_info;
 
  -- 效率高于单条 fetch
  FETCH cur_stu_info BULK COLLECT
    INTO v_stu_info_rows LIMIT 3; -- limit 3:每次插入 3 条 [可选项]
    
  -- 输出验证
  FOR i IN v_stu_info_rows.first .. v_stu_info_rows.last LOOP
    dbms_output.put_line('sno: ' || v_stu_info_rows(i).sno || ', sname: ' || v_stu_info_rows(i)
                         .sname || ', sex: ' || v_stu_info_rows(i).sex);
  END LOOP;

  CLOSE cur_stu_info;
END;

输出结果:

sno: 1, sname: 瑶瑶, sex: 女
sno: 2, sname: 优优, sex: 男
sno: 3, sname: 倩倩, sex: 女

2.3 在 returning into 中

declare
  type stu_info_table is table of scott.stu_info%rowtype;
  v_stu_info_rows stu_info_table;
begin
  -- delete 验证 (update、insert 同理)
  delete from stu_info si
   where si.sex = '女' return si.sno, si.sname, si.sex bulk collect into
   v_stu_info_rows;

  dbms_output.put_line('删除了 ' || sql%rowcount || ' 行记录');
  -- commit;

  -- 输出验证
  if v_stu_info_rows.count > 0 then
    for i in v_stu_info_rows.first .. v_stu_info_rows.last loop
      dbms_output.put_line('sno: ' || v_stu_info_rows(i).sno ||
                           ', sname: ' || v_stu_info_rows(i).sname ||
                           ', sex: ' || v_stu_info_rows(i).sex);
    end loop;
  end if;
end;

输出结果:

删除了 2 行记录
sno: 1, sname: 瑶瑶, sex: 女
sno: 3, sname: 倩倩, sex: 女

3 扩展:forall + bulk collect 综合运用

基础数据准备:

truncate table stu_info ;

insert into stu_info(sno, sname, sex) values(1, '瑶瑶', '女');
insert into stu_info(sno, sname, sex) values(2, '优优', '男');
insert into stu_info(sno, sname, sex) values(3, '倩倩', '女');
commit;

-- 测试表
create table stu_info_temp as select * from stu_info where 1 = 2;

开发实战:(将 stu_info 中的数据同步至 stu_info_temp)

-- 建议在 '测试窗口' Debug 瞅瞅下列执行步骤
DECLARE
  -- 声明游标
  CURSOR cur_stu_info IS
    SELECT si.sno, si.sname, si.sex FROM stu_info si;
  -- 定义基于游标类型的嵌套表 (也可用 RECORD 替换,只是写法会稍微麻烦点)
  TYPE stu_info_table IS TABLE OF cur_stu_info%ROWTYPE;
  -- 声明变量
  v_stu_info_rows stu_info_table;
BEGIN
  -- 开启游标
  OPEN cur_stu_info;

  LOOP
    -- 获取 cur_stu_info 数据
    FETCH cur_stu_info BULK COLLECT
      INTO v_stu_info_rows LIMIT 2; -- 数据有限,仅做测试,一般限制 500 左右
  
    EXIT WHEN v_stu_info_rows.count = 0; -- 注意此时游标退出使用了 xx.count,而不是 xx%notfound  
  
    -- 批量插入
    FORALL i IN 1 .. v_stu_info_rows.count
      INSERT INTO stu_info_temp VALUES v_stu_info_rows (i);
  END LOOP;

  -- 关闭游标
  CLOSE cur_stu_info;

  -- 提交数据
  COMMIT;
EXCEPTION
  WHEN OTHERS THEN
    -- 异常时,也要关闭游标
    IF cur_stu_info%ISOPEN THEN
      CLOSE cur_stu_info;
    END IF;
  
    dbms_output.put_line(dbms_utility.format_error_backtrace);
    dbms_output.put_line(SQLCODE || ', ' || SQLERRM);
END;
  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鱼丸丶粗面

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值