`

批量SQL(原创)

 
阅读更多

批量sql概述

对 PL/SQL而言,任何的PL/SQL块或者子程序都是PL/SQL引擎来处理,而其中包含的SQL语句则由PL/SQL引擎发送SQL语句转交到SQL 引擎来处理,SQL引擎处理完毕后向PL/SQL引擎返回数据。Pl/SQL与SQL引擎之间的通信则称之为上下文切换。过多的上下文切换将带来过量的性 能负载。因此为减少性能的FORALL与BULK COLLECT的子句应运而生,即仅仅使用一次切换多次执行来降低上下文切换次数。

注意:并不是所有的SQL,FORALL语句都会一次性将SQL转交到SQL引擎来处理。具体可参见下面的例子2,例子3

FORALL

FORALL语法描述
FORALL loop_counter IN bounds_clause            -->注意FORALL块内不需要使用loop, end loop
SQL_STATEMENT [SAVE EXCEPTIONS];
bounds_clause的形式
lower_limit .. upper_limit                                     -->指明循环计数器的上限和下限,与for循环类似
INDICES OF collection_name BETWEEN lower_limit .. upper_limit  -->引用特定集合元素的下标(该集合可能为稀疏,
即当集合(嵌套表或联合数组)中的元素被删除之后,对稀疏集合实现迭代。)
VALUES OF colletion_name                             -->
VALUES OF选项可以指定FORALL语句中循环计数器的值来自于指定集合中元素的值。VALUES OF选项使用时有一些限制

如果VALUES OF子句中所使用的集合是联合数组,则必须使用PLS_INTEGER和BINARY_INTEGER进行索引;
VALUES OF 子句中所使用的元素必须是PLS_INTEGER或BINARY_INTEGER;
当VALUES OF 子句所引用的集合为空,则FORALL语句会导致异常;

SQL_STATEMENT部分:SQL_STATEMENT部分必须是一个或者多个集合的静态或者动态的DML(insert,update,delete)语句。
SAVE EXCEPTIONS部分:对 于任意的SQL语句执行失败,将导致整个语句或整个事务会滚。而使用SAVE EXCEPTIONS可以使得在对应的SQL语句异常的情形下,FORALL仍然可以继续执行。如果没有SAVE EXCEPTIONS时,当异常发生,FORALL语句就会停止执行。SAVE EXCEPTIONS使得FORALL子句中的DML下产生的所有异常都记录在SQL%BULK_EXCEPTIONS的游标属性中。 SQL%BULK_EXCEPTIONS属性是个记录集合,其中的每条记录由两个字段组成,ERROR_INDEX和ERROR_CODE。 ERROR_INDEX字段会存储发生异常的FORALL语句的迭代编号,而ERROR_CODE则存储对应异常的ORACLE错误代码。类似于这样: (2,01400),(6,1476)和(10,12899)。存放在%BULK_EXCEPTIONS中的值总是与最近一次FORALL语句执行的结果 相关,异常的个数存放在%BULK_EXCEPTIONS.COUNT属性中,%BULK_EXCEPTIONS有效的下标索引范围在1 到%BULK_EXCEPTIONS.COUNT之间。

%BULK_ROWCOUNT 也是专门为FORALL设计的,用于保存第i个元素第i次insert或update或delete所影响到的行数。如果第i次操作没有行被影响, 则%BULK_ROWCOUNT返回为零值。FORALL语句和%BULK_ROWCOUNT属性使用同样的下标索引。如果FORALL使用下标索引的范 围在5到8的话,那么 
%BULK_ROWCOUNT的也是5到8。需要注意的是一般情况下,对于insert .. values而言,所影响的行数为1,即%BULK_ROWCOUNT的值为1。而对于insert .. select方式而言,%BULK_ROWCOUNT的值就有可能大于1。update与delete语句存在0,1,以及大于1的情形。 

FORALL综合示例

示例1,循环效率和indices of

SQL> CREATE TABLE t  ( 
   col_num   NUMBER 
  ,col_var   VARCHAR2( 10 )  ); 

SQL>  declare
  2     type col_num_type is table of number index by pls_integer;       -->声明了两个联合数组
  3     type col_var_type is table of varchar2(10) index by pls_integer;
  4     col_num_tab col_num_type;
  5     col_var_tab col_var_type;
  6     v_start_time integer;
  7     v_end_time   integer;
  8     v_total      integer;
  9  begin
 10    for i in 1..5000 loop      -->使用FOR循环向数组填充元素 
 11      col_num_tab(i) := i;
 12      col_var_tab(i) := 'var_' || i ;
 13    end loop;
 14    v_start_time := dbms_utility.get_time;     -->获得FOR循环向表t插入数据前的初始时间
 15    for i in 1..5000 loop     -->使用FOR循环向表t插入数据
 16      insert into t
 17      values(col_num_tab(i),col_var_tab(i));
 18    end loop;
 19    v_end_time := dbms_utility.get_time; rollback;
   -->获得FOR循环向表t插入数据前的结束时间

 20    dbms_output.put_line('Duration of the for loop: ' || (v_end_time - v_start_time));
 21    v_start_time := dbms_utility.get_time;    
-->获得FOR循环向表t插入数据前的初始时间

 22    forall i in 1..5000       -->使用FORALL循环向表t插入数据
 23      insert into t
 24      values(col_num_tab(i),col_var_tab(i));
 25    v_end_time :=dbms_utility.get_time; rollback;   
-->获得FORALL循环向表t插入数据前的结束时间

26    dbms_output.put_line('Duration of the forall statement: ' || (v_end_time - v_start_time));
 27    col_num_tab.delete(2);col_num_tab.delete(4);col_num_tab.delete(6);      -->此处删除了数组中的第二个元素,导致数组变为稀疏型
 28    col_var_tab.delete(2);col_var_tab.delete(4);col_var_tab.delete(6);
 29    forall i in indices of col_num_tab    -->此处使用了indices OF empno_tab,则所有未被delete的元素都将进入循环
 30       insert into t2
 31       values(col_num_tab(i),col_var_tab(i));
 32    select count(*) into v_total from t2;
 33    dbms_output.put_line('The amount rows of t2: ' || v_total);
 34*   end;
 35  /
Duration of the for loop: 105   -->此处的计时单位为百分之一秒,即1.05s,下同 
Duration of the forall statement: 2
The amount rows of t2: 4997    
-->此处少了3条记录。

PL/SQL procedure successfully completed.

示例2,save exception和values of

SQL> create table tb_emp_ins_log(empno number,ename varchar2(50),hiredate date); 
-->创建表tb_emp_ins_log用于记录错误记录 

Table created.

SQL> CREATE TABLE tb_emp AS              -->创建表tb_emp 
   SELECT empno, ename, hiredate 
   FROM   emp 
   WHERE  1 = 2; 

SQL> ALTER TABLE tb_emp MODIFY(empno NOT NULL);   -->为表添加约束 
SQL> declare
  2     type col_num_type is table of number index by pls_integer;    -->一共定义了4个联合数组类型 
  3     type col_var_type is table of varchar2(100) index by pls_integer;
  4     type col_date_type is table of date index by pls_integer;
  5     type ins_log_type is table of pls_integer index by pls_integer;   -->用于填充异常记录的元素值 
  6     empno_tab col_num_type;
  7     ename_tab col_var_type;
  8     hiredate_tab col_date_type;
  9     ins_log ins_log_type;
 10     v_counter pls_integer := 0;
 11     v_total integer := 0;
 12     errors exception;       -->异常声明 
 13     pragma exception_init( errors, -24381);
 14  begin
 15    for rec in (select empno,ename,hiredate from emp) loop    -->使用for循环将数据填充到联合数组
 16      v_counter := v_counter + 1 ;
 17      empno_tab(v_counter) := rec.empno;
 18      ename_tab(v_counter) := rec.ename;
 19      hiredate_tab(v_counter) := rec.hiredate;
 20    end loop;
 21   
 22    empno_tab(2) := null;      -->对部分数据进行处理以产生异常
 23    ename_tab(5) := rpad(ename_tab(5),15,'*');
 24    empno_tab(10) := null;
 25   
 26    forall i in 1..empno_tab.count    -->使用forall将联合数组中的数据插入到表tb_emp 
 27    save exceptions
 28      insert into tb_emp
 29      values(empno_tab(i),ename_tab(i),hiredate_tab(i));
 30    commit;

 32    exception
 33      when errors then
 34        dbms_output.put_line('There are ' || SQL%bulk_exceptions.count || ' exceptions');
 35        for i in 1..SQL%bulk_exceptions.count loop  -->SQL%bulk_exceptions.COUNT记录异常个数来控制迭代 
 36          ins_log(i) := SQL%bulk_exceptions(i).error_index;        -->异常记录的索引值将填充ins_log_type联合数组,此处的结果是ins_log(1)=2,  ins_log(2)=5,  ins_log(3)=10
 38          dbms_output.put_line(
 39                                'Record '
 40                                || SQL%bulk_exceptions(i).error_index
 41                                || ' caused error '
 42                                || i
 43                                || '; '
 44                                || SQL%bulk_exceptions(i).error_code
 45                                || ' '
 46                                || SQLERRM( -SQL%bulk_exceptions(i).error_code));   -->使用SQLERRM根据错误号抛出具体的错误信息
 47        end loop;
 49     for i in empno_tab.first .. empno_tab.last loop
 50        dbms_output.put_line(SQL%bulk_rowcount(i) || ' rows are inserted into tb_emp!');
 51     end loop;
 52     select count(*) into v_total from tb_emp;
 53     dbms_output.put_line(v_total || ' rows are inserted into tb_emp!');

 55     forall i in values of ins_log    -->使用VALUES OF子句为ins_log_type联合数组中的元素值 
 56        insert into tb_emp_ins_log
 57        values(empno_tab(i),ename_tab(i),hiredate_tab(i));  -->异常记录的索引值将填充ins_log_type联合数组 
 58     commit;
 60  end;
 61  /
There are 3 exceptions
Record 2 caused error 1; 1400 ORA-01400: cannot insert NULL into ()
Record 5 caused error 2; 12899 ORA-12899: value too large for column  (actual: , maximum: )
Record 10 caused error 3; 1400 ORA-01400: cannot insert NULL into ()
1 rows are inserted into tb_emp!
0 rows are inserted into tb_emp!
1 rows are inserted into tb_emp!
1 rows are inserted into tb_emp!
0 rows are inserted into tb_emp!
1 rows are inserted into tb_emp!
1 rows are inserted into tb_emp!
1 rows are inserted into tb_emp!
1 rows are inserted into tb_emp!
0 rows are inserted into tb_emp!
1 rows are inserted into tb_emp!
1 rows are inserted into tb_emp!
1 rows are inserted into tb_emp!
1 rows are inserted into tb_emp!
11 rows are inserted into tb_emp!
PL/SQL procedure successfully completed.
SQL> select * from tb_emp_ins_log;    -->异常的记录被插入到表tb_emp_ins_log 
     EMPNO ENAME           HIREDATE
---------- --------------- ------------------
           ALLEN           20-FEB-81
      7654 MARTIN********* 28-SEP-81
           TURNER          08-SEP-81

FROALL语句SQL%BULK_COUNT的使用可以参见下面例子:

例子3

create table emp_temp as select * from emp;
declare
  TYPE numlist is table of NUMBER;
  depts numlist := numlist(10,20,30);
begin
  forall j in depts.first .. depts.last
    delete from emp_temp where deptno = depts(j);
  for i in depts.first .. depts.last loop
    dbms_output.put_line('Statement #' || i || ' deleted ' ||
    SQL%BULK_ROWCOUNT(i) || ' rows.');
  end loop;
  dbms_output.put_line('Total rows deleted: ' || SQL%ROWCOUNT);
END;
/
Statement #1 deleted 3 rows.
Statement #2 deleted 5 rows.
Statement #3 deleted 6 rows.
Total rows deleted: 14
PL/SQL procedure successfully completed.

BULK COLLECT

BULK COLLECT 子句会批量检索结果,即一次性将结果集绑定到一个集合变量中,并从SQL引擎发送到PL/SQL引擎。通常可以在SELECT INTO、FETCH INTO以及RETURNING INTO子句中使用BULK COLLECT。

使用LIMIT限制FETCH数据量
在 使用BULK COLLECT 子句时,对于集合类型,如嵌套表,联合数组等会自动对其进行初始化以及扩展(如下示例)。因此如果使用BULKCOLLECT子句操作集合,则无需对集合 进行初始化以及扩展。由于BULK COLLECT的批量特性,如果数据量较大,而集合在此时又自动扩展,为避免过大的数据集造成性能下降,因此使用limit子句来限制一次提取的数据量。 limit子句只允许出现在fetch操作语句的批量中。
用法:
FETCH ... BULK COLLECT INTO ... [LIMIT rows]

RETURNING子句的批量绑定
BULK COLLECT除了与SELECT,FETCH进行批量绑定之外,还可以与INSERT,DELETE,UPDATE语句结合使用。当与这几个DML语句结合时,我们需要使用RETURNING子句来实现批量绑定。

BULK COLLECT综合示例

SQL> declare
  2    cursor emp_cur is
  3       select empno,ename,hiredate from emp;
  4    type emp_rec_type is record(
  5         empno emp.empno%type,
  6         ename emp.ename%type,
  7         hiredate emp.hiredate%type);
  type nested_emp_type is table of emp_rec_type;
  emp_tab1 nested_emp_type;
  emp_tab1 nested_emp_type;
  emp_tab2 nested_emp_type;
  v_limit integer := 5;
  v_counter integer := 0;
begin
  open emp_cur;
  loop
    fetch emp_cur 
-->fetch时使用了BULK COLLECT子句,并使用limit子句限制提取数据量     

   bulk collect into emp_tab1
    limit v_limit;
 19    dbms_output.put_line('The recycle time : ' || v_counter);
  exit when emp_tab1.count = 0;  --
>注意此时游标退出使用了emp_tab.COUNT,而不是emp_cur%notfound 

    delete from emp where deptno = 20
    v_counter := v_counter + 1;     -->记录使用LIMIT之后fetch的次数 
  end loop;
  dbms_output.put_line('The recycle time : ' || v_counter);
 
    delete from emp where deptno = 20
    returning empno,ename,hiredate
    bulk collect into emp_tab2;tab2;     -->使用returning将前面返回的列的数据批量插入到集合变量
 27 
    end loop;i in emp_tab2.first..emp_tab2.last loop
    for i in emp_tab2.first..emp_tab2.last loop
       dbms_output.put_line('Current record : ' || emp_tab2(i).empno
                         || '     '
                         || emp_tab2(i).ename
                         || '     '
                         || emp_tab2(i).hiredate
                         || ' hase been deleted');
    end loop;
 36  end;
 37  /
The recycle time : 3
Current record : 7369     SMITH     17-DEC-80 hase been deleted
Current record : 7566     JONES     02-APR-81 hase been deleted
Current record : 7788     SCOTT     19-APR-87 hase been deleted
Current record : 7876     ADAMS     23-MAY-87 hase been deleted
Current record : 7902     FORD     03-DEC-81 hase been deleted
PL/SQL procedure successfully completed.

BULK COLLECT的限制
1、不能对使用字符串类型作键的关联数组使用BULK COLLECT 子句。
2、只能在服务器端的程序中使用BULK COLLECT,如果在客户端使用,就会产生一个不支持这个特性的错误。
3、BULK COLLECT INTO 的目标对象必须是集合类型。
4、复合目标(如对象类型)不能在RETURNING INTO 子句中使用。
5、如果有多个隐式的数据类型转换的情况存在,多重复合目标就不能在BULK COLLECT INTO 子句中使用。
6、如果有一个隐式的数据类型转换,复合目标的集合(如对象类型集合)就不能用于BULK COLLECTINTO 子句中。

 

参考至:《Oracle PL/SQL实例精解》Benjamin著
            http://blog.csdn.net/robinson_0612/article/details/7536926
            http://blog.csdn.net/robinson_0612/article/details/7545597
本文原创,转载请注明出处、作者
如有错误,欢迎指正
邮箱:cmzcj@163.com

1
0
分享到:
评论

相关推荐

    sqleditor 一个用c#写的db2客户端工具(原创)

    综上所述,这个原创的C# DB2客户端工具结合了.NET Framework 2.0的强大功能,提供了丰富的数据库管理特性,如智能代码提示、批量SQL执行、数据编辑以及导出功能,旨在提升DB2数据库的管理和操作体验。

    学生信息管理系统(SQL后台+原创)

    学生信息管理系统是一款基于SQL数据库技术开发的原创软件应用,主要用于教育机构或学校对学生数据进行有效管理和维护。系统设计的核心目标是提高数据管理效率,确保信息的准确性和安全性,同时简化日常的学生信息...

    Oracle SQL Handler(Oracle 开发工具) v5.1.zip

    (8) 监控批量SQL语句的运行,在运行中可暂停、更正等; (9) 以缩进的阶梯格式清晰地显示SQL的解释计划; (10)方便操作常用数据字典如表、视图、索引、过程、函数、触发器等(能查看和编辑编译PL/SQL代码);...

    原创经销存管理系统,asp+sql2000

    【标题】"原创经销存管理系统,asp+sql2000"揭示了这是一个基于ASP(Active Server Pages)技术,结合SQL Server 2000数据库的库存管理解决方案。ASP是微软开发的一种服务器端脚本环境,用于创建动态交互式网页。SQL...

    超级列表框批量导入Access数据库例程源码

    标签"SanYe"可能是资源作者的标识,可能是这个代码的原创者或者开发者,但具体信息需要额外的背景知识。 在压缩包内的"content.txt"文件很可能是源代码的文本内容,包含了上述描述的功能实现。为了深入理解这个例程...

    SQL2005重新生成索引的的存储过程 sp_rebuild_index 原创

    标题中的"SQL2005重新生成索引的的存储过程 sp_rebuild_index 原创"指的是在SQL Server 2005中,作者创建了一个名为`sp_rebuild_index`的自定义存储过程,目的是方便高效地批量重新生成数据库中的索引。这通常用于...

    原创易语言连接数据库,实现增、删、改、查数据库,一键导入数据库源码

    易语言中,通过构造SQL字符串,然后调用执行SQL的函数,如`数据库执行SQL`,来完成插入操作。 2. 删除数据(Remove):使用SQL的DELETE语句,删除满足特定条件的数据。同样,通过构建相应的SQL字符串并执行。 3. ...

    基于jsp的时装原创设计交流网.zip

    sql 功能模块分析 原创设计师管理模块: a.用户注册:设计师必须注册以后才能发布有关信息; b.用户信息修改:设计师可以修改其联系方式等信息; c.发布时装信息:设计师可以发布有关信息; d.接收信息:设计师...

    php读取txt文件组成SQL并插入数据库的代码(原创自Zjmainstay)

    首先,我们要明确本次讨论的代码由Zjmainstay原创提供,目的是为了帮助那些需要将TXT文件中的数据导入到数据库中。在这段代码中,涉及到几个关键的函数和方法,包括`file_get_contents()`函数、`preg_replace()`和`...

    vb原创图书管理系统(源代码+论文).zip

    同时,对于涉及多条数据库操作的事务,如批量修改图书信息,系统会使用SQL的事务处理机制,确保数据的一致性和完整性。 九、论文部分 提供的“论文”可能详细介绍了系统的设计思路、实现方法、技术选型、性能评估等...

    EXCEL万能百宝箱64位 V29.0珍藏版—智慧轻巧办公,批量打印二维码条形码,十倍提效率,一码三用,480个功能.rar

    原创批量图片排序功能,誉为“图片排序大师”,有21种样式可选,能批量将工作表中指定存储格范围的图片快速排序并输出到新表中。支持XP至WIN10多个操作系统完美兼容,实现无缝隙对接,U盘版用户除了在原有可换机使用...

    原创的JDBC封装可支持各种数据库

    支持批量SQL执行,提高性能。封装一个方法接收SQL语句数组,一次性执行多个SQL。 10. **连接关闭**: 使用连接池时,不需要手动关闭连接,但在不使用连接池的情况下,需要确保在操作完成后关闭连接、Statement和...

    spring 整合JDBC 原创代码

    对于批量操作,JdbcTemplate提供了`batchUpdate(String[] sqls)`方法,可以一次执行多条SQL语句,提高效率。 8. **原创代码示例** 在压缩包中的"复件 SpringJDBC"文件可能包含一个简单的Spring整合JDBC的示例。...

    Tutorialspoint Selenium SQLServer Swift Solr SVN SQLite Socket Smarty Shell 教程

    10. **Sed**:Sed(流编辑器)是用于文本处理的工具,常用于批量替换、删除、插入文本等操作。学习Sed,你可以熟练运用正则表达式来处理大量文本数据,提高文本处理效率。 这些教程覆盖了软件开发的多个重要方面,...

    VB_题库管理系统_原创

    本VB题库管理系统是一款完全由个人原创开发的软件,旨在为用户提供方便、高效的方式来管理和组织各类题目,适用于教学、培训、考试等多种场景。它基于Visual Basic(VB)编程语言,利用VB强大的图形用户界面(GUI)...

    Oracle 基础知识 -大全- 原创整理.pdf

    Oracle使用GRANT和REVOKE语句管理用户权限,角色是权限的集合,方便权限的批量授予和撤销。 15. **PL/SQL编程** PL/SQL是Oracle特有的过程式语言,结合SQL用于编写存储过程、函数、触发器等。它支持异常处理、...

    基于ASP的批量替换采集文章同义词ACCESS版 v1.0.zip

    在"基于ASP的批量替换采集文章同义词ACCESS版 v1.0.zip"这个项目中,我们可以推测这是一款利用ASP技术构建的工具,其功能主要是批量处理采集到的文章,通过同义词替换来提高内容的原创性和多样性。这在SEO(搜索引擎...

Global site tag (gtag.js) - Google Analytics