`
sangei
  • 浏览: 337297 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

(转)批量绑定—BULK COLLECT

阅读更多
在 PL/SQL 编码中,经常会从表中获取结果集,然后进行一些逻辑处理,再生成新的数据。我们想到的最直接的方法就是使用游标。从 Oracle8i 开始,出现了一个新的子句:BULK COLLECT。可以降低 SQL 引擎到 PL/SQL 引擎的上下文交换(context switch)次数,从而实现数据的高速检索。
 
1. 速度比较
 
从表中获取结果集,我们常用的方法就是使用游标循环,我们看看它的执行速度:

 

SQL> set serveroutput on

SQL> declare

  2    type tnt_ic01 is table of ic01%rowtype;

  3    nt_ic01 tnt_ic01 := tnt_ic01();

  4    n_startTime number;

  5  begin

  6    n_startTime := dbms_utility.get_time;

  7    for rec_ic01 in( select * from ic01 where rownum < 100000 ) loop

  8      nt_ic01.extend;

  9      nt_ic01( nt_ic01.last ) := rec_ic01;

 10    end loop;

 11    dbms_output.put_line( 'COUNT: ' || nt_ic01.count );

 12    dbms_output.put_line( 'USED TIEM: ' || ( dbms_utility.get_time - n_startTime ) );

 13  end;

 14  /

COUNT: 99999

USED TIEM: 255

 

PL/SQL 过程已成功完成。

 

我们再看一下完成同样的功能,BULK COLLECT 的执行速度:

 

SQL> declare

  2    type tnt_ic01 is table of ic01%rowtype;

  3    nt_ic01 tnt_ic01;

  4    n_startTime number;

  5  begin

  6    n_startTime := dbms_utility.get_time;

  7    select *

  8      bulk collect into nt_ic01

  9      from ic01

 10     where rownum < 100000;

 11    dbms_output.put_line( 'COUNT: ' || nt_ic01.count );

 12    dbms_output.put_line( 'USED TIEM: ' || ( dbms_utility.get_time - n_startTime ) );

 13  end;

 14  /

COUNT: 99999

USED TIEM: 159

 

PL/SQL 过程已成功完成。

 
BULK COLLECT 要比游标循环快得多。如果你的代码中有游标循环,并且你也正为这里执行缓慢而发愁,不妨试试 BULK COLLECT。
 
2. 没有 NO_DATA_FOUND 异常
 
不过要记得,SELECT ... BULK COLLECT INTO ... 不会引起 NO_DATA_FOUND 异常,虽然它的语法看起来和 SELECT ... INTO ... 类似。要判断 BULK COLLECT 是否获得数据,可以通过 COUNT 方法

 

SQL> declare

  2    type tnt_ic01 is table of ic01%rowtype;

  3    nt_ic01 tnt_ic01;

  4    rec_ic01 ic01%rowtype;

  5  begin

  6    --1. normal select, can cause no_data_found

  7    begin

  8      select *

  9        into rec_ic01

 10        from ic01

 11       where 1 = 0;

 12    exception

 13      when no_data_found then

 14        dbms_output.put_line( 'error1: no_data_found exception accured!' );

 15    end;

 16

 17    --2. bulk collect, won't cause no_data_found

 18    select *

 19      bulk collect into nt_ic01

 20      from ic01

 21     where 1 = 0;

 22    if nt_ic01.count = 0 then

 23      dbms_output.put_line( 'error2: There is no data in nt_ic01.' );

 24    end if;

 25  end;

 26  /

error1: no_data_found exception accured!

error2: There is no data in nt_ic01.

 

PL/SQL 过程已成功完成。

 

3. 其他使用
 
除了 SELECT INTO 外,还可以在 FETCH INTO 和 RETURNING INTO 子句中使用 BULK COLLECT:
 

-- 1. fetch collect into

SQL> declare

  2    cursor cur_ic01 is

  3      select *

  4        from ic01

  5       where rownum < 10;

  6

  7    type tnt_ic01 is table of cur_ic01%rowtype;

  8    nt_ic01 tnt_ic01;

  9  begin

 10    open cur_ic01;

 11    fetch cur_ic01 bulk collect into nt_ic01;

 12    close cur_ic01;

 13    dbms_output.put_line( 'COUNT: ' || nt_ic01.count );

 14  end;

 15  /

COUNT: 9

 

PL/SQL 过程已成功完成。

 

-- 2. returning into

SQL> select * from test_return;

 

AAC001               AAC003
-------------------- ----------
1304012080949        转入人7
1304012080950        转入人8
1304012080951        转入人9
1304012080952        转入

 

SQL> declare
  2    type tnt_return is table of test_return%rowtype;
  3    nt_return tnt_return;
  4  begin
  5    update test_return
  6       set aac003 = 'yuechaotian'
  7     where rownum < 3
  8    
returning aac001, aac003 bulk collect into nt_return;
  9
 10    for n_pointer in 1..nt_return.count loop
 11      dbms_output.put_line( 'AAC001=' || nt_return(n_pointer).aac001
 12                      || ' | AAC003=' || nt_return(n_pointer).aac003 );
 13    end loop;
 14  end;
 15  /
AAC001=1304012080949 | AAC003=yuechaotian
AAC001=1304012080950 | AAC003=yuechaotian

 

PL/SQL 过程已成功完成。

 

SQL> select * from test_return;

AAC001               AAC003
-------------------- ------------
1304012080949        yuechaotian
1304012080950        yuechaotian
1304012080951        转入人9
1304012080952        转入

SQL>

 
4. 联合数组中使用 BULK COLLECT
 
前面我们举的例子,都是用的嵌套表,其实也可以使用联合数组:

 

 

SQL> declare

  2    cursor cur_ic01 is

  3      select *

  4        from ic01

  5       where rownum < 10;

  6

  7    type tnt_ic01 is table of cur_ic01%rowtype index by pls_integer;

  8    nt_ic01 tnt_ic01;

  9  begin

 10    open cur_ic01;

 11    fetch cur_ic01 bulk collect into nt_ic01;

 12    close cur_ic01;

 13    dbms_output.put_line( 'COUNT: ' || nt_ic01.count );

 14  end;

 15  /

COUNT: 9

 

PL/SQL 过程已成功完成。

 

 

BULK COLLECT 生成的集合,下表是默认从1开始的数字,步进为1,所以联合数组的索引,不能使用 varchar2 类型。如果你不小心使用了 varchar2 类型作为联合数组的索引,就会出现下面的错误:

 

Oracle9i 之前,BULK COLLECT 只能用在静态 SQL 中,从 Oracle9i 开始,动态 SQL 中也可以使用 BULK COLLECT:

 

SQL> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
PL/SQL Release 10.2.0.1.0 - Production
CORE    10.2.0.1.0      Production
TNS for 32-bit Windows: Version 10.2.0.1.0 - Production
NLSRTL Version 10.2.0.1.0 - Production

SQL> declare

  2    type tnt_ic01 is table of ic01%rowtype;

  3    nt_ic01 tnt_ic01;

  4

  5    v_sql_cursor varchar2(1000);

  6  begin

  7    v_sql_cursor := 'select * from ic01 where rownum < 100000';

  8    execute immediate v_sql_cursor bulk collect into nt_ic01;

  9    dbms_output.put_line( 'COUNT: ' || nt_ic01.count );

 10  end;

 11  /

COUNT: 99999

 

PL/SQL 过程已成功完成。

 

SQL>

 

 

ERROR 位于第 11 :

ORA-06550: 11 , 36 :

PLS-00657: 实施约束: 不支持带有 VARCHAR2 关键字的关联性数组的批 SQL

 

 

5. VARRAY 数组中使用 BULK COLLECT

 

而对于 VARRAY 数组,BULK COLLECT 也适用,不过定义时,其长度必须得大于或等于结果集的长度:

 

 

SQL> declare

  2    cursor cur_ic01 is

  3      select *

  4        from ic01

  5       where rownum < 10;

  6

  7    type tnt_ic01 is varray(9) of ic01%rowtype;

  8    nt_ic01 tnt_ic01;

  9  begin

 10    open cur_ic01;

 11    fetch cur_ic01 bulk collect into nt_ic01;

 12    close cur_ic01;

 13    dbms_output.put_line( 'COUNT: ' || nt_ic01.count );

 14  end;

 15  /

COUNT: 9

 

PL/SQL 过程已成功完成。

 

 

如果 VARRAY 数组长度定义得过小,则会提示错误:

 

 

SQL> declare

  2    cursor cur_ic01 is

  3      select *

  4        from ic01

  5       where rownum < 10;

  6

  7    type tnt_ic01 is varray(8) of ic01%rowtype;

  8    nt_ic01 tnt_ic01;

  9  begin

 10    open cur_ic01;

 11    fetch cur_ic01 bulk collect into nt_ic01;

 12    close cur_ic01;

 13    dbms_output.put_line( 'COUNT: ' || nt_ic01.count );

 14  end;

 15  /

declare

*

ERROR 位于第 1 :

ORA-22165: 指定的索引 [9] 必须介于 [1] [8] 之间

ORA-06512: line 11

 

 

如果 VARRAY 数组长度定义得过大,没什么影响,而且也不会占用多余的内存空间:

 

 

SQL> declare

  2    cursor cur_ic01 is

  3      select *

  4        from ic01

  5       where rownum < 10;

  6

  7    type tnt_ic01 is varray(10) of ic01%rowtype;

  8    nt_ic01 tnt_ic01;

  9  begin

 10    open cur_ic01;

 11    fetch cur_ic01 bulk collect into nt_ic01;

 12    close cur_ic01;

 13    dbms_output.put_line( 'COUNT: ' || nt_ic01.count );

 14    dbms_output.put_line( 'LIMIT: ' || nt_ic01.limit );

 15  end;

 16  /

COUNT: 9

LIMIT: 10

 

PL/SQL 过程已成功完成。

 

 

不过推荐首选联合数组,或是嵌套表,在这里虽然可以使用 VARRAY 数组,但显然是不太合适的,就像steven 所介绍的那样。

 

6. 动态 SQL 中使用 BULK COLLECT

 

分享到:
评论

相关推荐

    oracle下巧用bulk collect实现cursor批量fetch的sql语句

    在Oracle数据库中,Bulk Collect是一个非常实用的特性,它允许我们一次从游标(Cursor)中批量地获取多行数据,极大地提高了处理大量数据时的性能。本文将深入讲解如何在PL/SQL中使用Bulk Collect实现Cursor的批量...

    Oracle BULK COLLECT批量取数据解决方法

    Oracle BULK COLLECT是Oracle数据库提供的一种高级PL/SQL特性,用于提高...在实际开发中,应当根据具体业务需求和数据规模灵活运用BULK COLLECT,结合其他优化策略,比如绑定变量、索引优化等,以达到最佳的性能效果。

    Oracle Bulk Binds技术分析.pdf

    批量绑定技术的核心在于减少PL/SQL和SQL引擎之间的上下文切换次数,从而提高效率。在传统的SQL语句执行过程中,每次SQL的执行都需要PL/SQL引擎和SQL引擎之间的切换,这会消耗一定的系统资源。而批量绑定则一次性处理...

    批量插入数据到Oracle数据库Demo

    例如,我们可以从CSV文件中读取数据,然后使用BULK COLLECT INTO将这些数据批量插入到表中。 3. **FORALL**:在收集了大量数据后,可以使用FORALL语句一次性将它们插入到数据库中,避免了循环中的单次插入,提高了...

    forall 用法小结

    其中,`FORALL`语句作为Oracle PL/SQL的一个强大特性,提供了批量绑定和批量收集的能力,能够显著提升处理大量数据时的效率。本文将深入探讨`FORALL`的使用方法及其背后的原理,帮助开发者更好地理解和应用这一功能...

    JAVA 与 Sql学习笔记

    它在发送SQL语句到SQL引擎之前,先将输入集合批量绑定。 2. **批绑定提高性能**: 批绑定通过一次性处理多个数据项,显著减少了PL/SQL与SQL引擎之间的通信次数。这包括三种类型的绑定操作: - in-bind(输入绑定...

    sql绑定变量

    2. **批量数据处理**:通过`bulk collect into rec`语句,一次获取多行数据到`rec`变量中,然后再遍历这个集合进行更新操作,这正是利用了绑定变量进行批量数据处理的典型应用。 3. **安全性与效率**:通过使用绑定...

    oracle性能

    1. **Oracle批量绑定(Bulk Bind)**:批量绑定是Oracle提供的一种优化SQL语句执行的方式,特别是对于大量数据操作。`FORALL`与`BULK COLLECT`是PL/SQL中的两个关键字,它们一起用于在存储过程或函数中执行批量DML操作...

    update语句的优化-oracle

    对于大量相似的更新操作,可以考虑使用BULK COLLECT和FORALL语句进行批量处理,以减少网络和数据库调用的开销: ```sql DECLARE TYPE col_table IS TABLE OF table_name.column_name%TYPE INDEX BY PLS_INTEGER; ...

    ORACLE数据库中插入大字段数据的解决方法

    - 使用`BULK COLLECT INTO`语句,可以一次收集多行数据到集合中,然后批量插入,减少与数据库的交互次数,提升性能。 - 使用`FORALL`语句与`BULK COLLECT`配合,可以避免循环中每次的DML操作,降低开销。 3. **...

    Oracle 游标的使用

    在Oracle 9i及其以后的版本中,还增加了使用BULK COLLECT子句批量绑定数据和使用CURSOR表达式实现嵌套游标的功能。本章将主要介绍如何使用显式游标进行多行数据的查询、游标FOR循环以及游标变量的使用,另外还将介绍...

    oracle 优化笔记

    "Oracle优化笔记"可能涵盖了如何有效地优化SQL查询,特别是针对where条件的编写技巧,以及如何利用FORALL和BULK COLLECT等特性来提升性能。下面我们将详细探讨这些关键知识点。 1. **SQL优化基础**: SQL优化的...

    plsql编程与优化

    批量绑定是指在一个操作中同时处理多条记录的技术。通过使用`FORALL`语句,可以在一次操作中插入、更新或删除多个记录。 示例代码: ```plsql DECLARE TYPE rec_type IS RECORD (id NUMBER, name VARCHAR2(50)); ...

    oracle动态函数执行.pdf

    当需要将查询结果集批量插入到一个PL/SQL表类型的变量`indexed_var`时,可以使用`BULK COLLECT INTO`子句。 在示例代码中,可以看到如何动态构建并执行函数调用。例如,`str_mult`函数的动态执行通过`EXECUTE ...

    Ocacle加速查询的存储过程

    集合写法(飞机速度)是通过一次性处理多个行来加速数据操作,如BULK COLLECT INTO和FORALL语句。这种技术将查询时间缩短到0.22秒,极大地提高了处理大量数据的速度。 5. **直接路径插入**: 直接路径插入避免了...

    Oracle_SQL学习笔记

    - **PL/SQL中的批量绑定变量**: - **FORALL循环**: FORALL语句用于执行批量更新、插入或删除操作。 - 示例: `FORALL i IN 1..x.COUNT INSERT INTO t VALUES (x(i));` - **BULK COLLECT**: BULK COLLECT用于收集多...

    Oracle批量插入数据的三种方式【推荐】

    在实际应用中,为了进一步提高性能,可以结合Oracle的`BULK COLLECT`和`FORALL`语句,以及使用绑定变量来处理大量数据。此外,对于超大规模的数据导入,可以考虑使用`SQL*Loader`工具,它能快速高效地从外部文件导入...

    oracle 10 学习资料.pptx

    `FORALL`关键字可以用于一次性对集合中的所有元素执行操作,而`BULK COLLECT`子句则用于在将结果集返回到PL/SQL之前进行批量绑定,这在处理查询结果时非常高效。 嵌套表是数据库中嵌套的表结构,可以直接使用SQL...

    Oracle 学习笔记

    5. **PL/SQL中的批量绑定变量(FORALL):** - `FORALL`循环可以用于批量插入、更新或删除数据。 ```sql FORALL i IN 1..x.COUNT INSERT INTO t (col1, col2) VALUES (x(i).col1, x(i).col2); ``` - 使用`SQL%...

    PLSQL笔记(EFRIOO&GUOH)1.doc

    #### 7.12.3 在 DELETE 语句上使用批量绑定 一次删除多个行。 ```sql FORALL i IN 1..v_employees.COUNT DELETE FROM employees WHERE employee_id = v_employees(i); ``` ### 十二、FORALL 语句 #### 7.13.1 在...

Global site tag (gtag.js) - Google Analytics