本站不再更新,欢迎光临 java开发技术网
随笔-230  评论-230  文章-8  trackbacks-0

可以在 PL/SQL 块的内部运行 SQL 语句,一般的方法是:

A 使用通常位于服务器端的 PL/SQL 引擎,做纯粹的 PL/SQL 执行。但是,客户端程序有它自己的 PL/SQL 引擎。

B 所有的 SQL DML 语句都被送到 SQL 引擎进行执行,结果和数据或 DML 执行后结果再传送加 PL/SQL 引擎。

 

这样导至的结果:当 SQL 语句在 PL/SQL 引擎和 SQL 引擎之间进行传送的时候,可能会发生上下文切换。在 PL/SQL 内执行 SQL 时,一件很重要的事情就是要保证查询最优化。由于代码往往是 PL/SQL 代码与 SQL 代码混合一起的,所以其执行过程还应该包括两种代码的解析。

 

不同引擎之间传送语句的操作被称为上下文切换,而上下文切换可能导致代码的执行达不到最佳。

 

解决这种情能问题的方法之一就是通过使用批绑定。在 Oracle 8i 中引入的批绑定,全面提高了 PL/SQL 的执行性能,在 Oracle9i 中又做了以下加强:

       如果涉及到 DML 的批绑定发生异常,使用 SAVE EXEPTIONS 子句处理仍可以连续运行。

       直接在单个 DML 语句中支持批动态 SQL 处理多行数据的能力。

可以与 RETURNING 子句一起使用动态 SQL ,支持多行更新和删除。在 oracle 9i 以前本地动态 SQL 只在单行输出才支持 RETURNING 子句。

 

 

接下来介绍可以提高程序执行性能的批绑定的各种特性,然后介绍以下语句的技术:

       DML

批查询

批动态 SQL

 

DML

       DML 是由使用 PL/SQL index-by 表或集合作为 DML 语句的输入、批量地执行 INSERT UPDATE DELETE 的语句组成的。可以使用 FORALL 语句执行批 DML ,如:

create or replace procedure batch_dml(errnum outnumber,errtext outvarchar2)
is
   type user_record isrecord( 
-- 定义记录类型
     id number,
     namevarchar2(
200 ),
     sex varchar2(
2 )
   );
   type user_tab istableof user_record indexbybinary_integer;
-- 定义记录类型的 index-by

   user_recs user_tab;
begin
    for idx in
1 .. 5 loop   -- 初始化数据
         user_recs(idx).id:=idx;
         user_recs(idx).name:=
'xxxxx' ||idx;
         user_recs(idx).sex:=
'F' ;
    endloop;
    deletefrom t_user;
    for idx in user_recs.first..user_recs.lastloop
        insertinto t_user values(user_recs(idx).id, user_recs(idx).name,user_recs(idx).sex);
    endloop;
    commit;
    errnum:=
0 ;
    errtext:=
'' ;
exception
    whenothersthen
         errnum:=sqlcode;
         errtext:=sqlerrm; 
end batch_dml;
对于这段代码, FOR LOOP 循环每执行一次,其中的 insert 语句就会被送到 SQL 引擎一次,也就发生一次上下文切换。这意味着当数据量很大的时候阻塞就会很严重,而这时 insert 语句也就会影响数据库更多的记录行,而使用批绑定可以很大程度上提高程的执行性能。

绑定指的是在 SQL 语句中为 PL/SQL 变量赋值,而一次性地将整个值集合绑定称为批绑定。批绑定能减少数据往返的次数,因些在 PL/SQL SQL 引擎之间上下文切换次数也会减少。这样显著提高程序的执行性能。可以使用 FORALL 语句来执行 DML 批绑定。其语法如下:

       FORALL index IN index-by.first..index-by.last  [save exceptions]

                     Sql_statement;

下面我们把上面的 batch_dml 改成批绑定的 DML ,代码如下

create or replace procedure batch_dml(errnum outnumber,errtext outvarchar2)
is
  
--type user_record is record(  --¶¨Òå¼Ç¼ÀàÐÍ
   
-- id number,
  
--  name varchar2(200),
  
--  sex varchar2(2)
 
--  );
  
--type user_tab is table of user_record index by binary_integer; --¶¨Òåuser_recordÀàÐ͵Äindex-by±íÀàÐÍ
  
--user_recs user_tab;
  
   type user_id_tab istableofnumberindexbybinary_integer;
   type user_name_tab istableofvarchar2(
20 ) indexbybinary_integer;
   type user_sex_tab istableofvarchar2(
2 ) indexbybinary_integer;
  
   user_id user_id_tab;
   user_name user_name_tab;
   user_sex user_sex_tab;
begin
    for idx in
1 .. 50000 loop   -- 初始化数据
         user_id(idx):=idx;
         user_name(idx):=
'xxx' ||idx;
         user_sex(idx):=
'F' ;
    endloop;
    deletefrom t_user;
    forall idx in user_id.first..user_id.last
        insertinto t_user values(user_id(idx), user_name(idx),user_sex(idx));
    commit;
    errnum:=
0 ;
    errtext:=
'' ;
exception

    when others then
         commit;

     errnum:=sqlcode;
         errtext:=sqlerrm; 
end batch_dml;
插入 50000 条记录仅用了 2.16

 

使用 FORALL 时,多个 INSERT UPDATE DELETE 语句是在一个数据库调用中完成的。这就缩短了程序的执行时间。只有单条引用了集合元素的 INSERT UPDATE DELETE 语句才可以使用 FORALL 进行执行。

 

SQL 出错以后

当发生 SQL 错误的时候,产生错误的那条 SQL 操作是以一种自动回滚的方式终止的,而接着程序的执行就停止下来。只有产生错误的这条 SQL 才可以回滚。而一旦执行成功,前的 SQL 语句是不再回滚了。

 

SAVE EXCEPTION 子句

Oracle 9i 中增强了批 DML 在发生异常时处理的错误及失败的程序执行能力。这是通过 FORALL 语句的 SAVE EXCEPTION 子句来实现,其语法如下:

 FORALL  index in lower..upper  save exceptions

 

SAVE EXCEPTIONS 子句是 oracle 9i 中引入的,它在一个隐式游标属性 SQL%BULK_EXCEPTIONS 中保存错误行,允许 FORALL 语句继续处理其余行。

下面这一段代码是 SQL%BULK_EXCEPTIONS 的应用例子:

create or replace procedure batch_dml(errnum outnumber,errtext outvarchar2)
is

 
   type user_id_tab istableofnumberindexbybinary_integer;
   type user_name_tab istableofvarchar2(
20 ) indexbybinary_integer;
   type user_sex_tab istableofvarchar2(
2 ) indexbybinary_integer;
  
   user_id user_id_tab;
   user_name user_name_tab;
   user_sex user_sex_tab;
  
   bulk_bind_excep EXCEPTION;
   pragmaexception_init(bulk_bind_excep,-
24381 );
begin
    for idx in
1 .. 50000 loop   --³õʼ»¯index-by¼Ç¼
         user_id(idx):=idx;
         user_name(idx):=
'xxx' ||idx;
         user_sex(idx):=
'F' ;
    endloop;
    user_id(
40000 ):= 39999 ;
    user_id(
10000 ):= 9999 ;
    deletefrom t_user;
    forall idx in user_id.first..user_id.lastsaveexceptions
        insertinto t_user values(user_id(idx), user_name(idx),user_sex(idx));
    errnum:=
0 ;
    errtext:=
'' ;
exception
    when bulk_bind_excep then
         for i in
1 ..sql%bulk_exceptions.countloop
             dbms_output.put_line(
'Iteration ' ||SQL%bulk_exceptions(i).error_index|| 'failed with error ' ||sqlerrm(sql%bulk_exceptions(i).error_code));
         endloop;
         commit;
    whenothersthen
         commit;
         errnum:=sqlcode;
         errtext:=sqlerrm; 
end batch_dml;
这个例子是修改了上面的程序,加上 save exceptions 异常处理,当批 dml 发生异常时也能正常运行。

 

FORALL 语句属性

与隐式游标的属性一样,批绑定操作也有与之关联的标量属性,这些标量属性有 %found %NOTFOUND %ROWCOUNT %ROWCOUND 属性是基于批绑定的 SQL 语句全部执行完成以后的。 %FOUND %NOTFOUND 指的是刚刚执行的那一条 SQL 语句。

 

批查询

可以使用 bulk collect into collection_name ,其中 collect_name index-by 表、嵌套表或 Varray 的名称。可以在 select into fetch into returning into 子句中使用 bulk collect 。可以在 into 例表中经引用多个类集。

 
select ..into 语句中使用 bulk  collect

语法如下:

Select column_name bulk collect into collect_name

例子:

create or replace procedure bulkcollectdemo(
    errnum outnumber,
    errtext outvarchar2
)
authidcurrent_user
is
 type uidtab istableof t_user.id%type;
 type unametab istableof t_user.name%type;
 id_tab uidtab;
 name_tab unametab;
begin
   select a.id,a.name  bulkcollectinto id_tab,name_tab from t_user a orderby a.id;
   for i in id_tab.first..id_tab.lastloop
      null;
   endloop;
   errnum:=
0 ;
   errtext:=
'' ;
exception
   whenothersthen
     errnum:=sqlcode;
     errtext:=sqlerrm;  
end;

使用 bulk collect 的主要优点是:它减少在 SQL PL/SQL 引擎之间的上下文切换次数,最大程度的提高程序执行的性能,在选择记录里可以使用 rownum 来限制加载的行数。实现这种功能的代码如下:

select id bulk collect into region_name where rownum<51;

 

Fetching 中使用 BULK COLLECT

从游标中取多行数据时,可以使用 bulk collect 。其语法如下:

 Fetch cursor_name bulk collect into collection_name

下面的例子是修改上一个存储过程的,其代码如下:

create or replace procedure bulkcollectdemo(
    errnum outnumber,
    errtext outvarchar2
)
authidcurrent_user
is
 type uidtab istableof t_user.id%type;
 type unametab istableof t_user.name%type;
 id_tab uidtab;
 name_tab unametab;
 cursor cur_tuser is
    select id,namefrom t_user orderby id;
begin
   open cur_tuser;
   fetch cur_tuser bulkcollectinto id_tab,name_tab;
   dbms_output.put_line(id_tab.count);
   for i in id_tab.first..id_tab.lastloop
      if i=
37 then
        dbms_output.put_line(
'------' );
      endif;
   endloop;
   close cur_tuser;
   errnum:=
0 ;
   errtext:=
'' ;
exception
   whenothersthen
     errnum:=sqlcode;
     errtext:=sqlerrm;  
end;

不能在 forall 语句中使用 select ... bulk collect 语句


returning into 中使使用 bulk collect

 

弄不明白暂时不管

 

 

批动态 SQL

本地动态 SQL 比静态 SQL 更优,它的执行也更快,在 Oracle 9i 之前,只能使用 DBMS_SQL 来执行批动态 SQL Oralce 9i 已经增强了本地动态 SQL ,使其支持批处理操作。

1 、通过使用 SELECT INTO 语句,将 BULK COLLECT EXECUTE IMMEDIATE 结合在一起进行批查询,其语法如下:

Execute immediate ‘select statement’ bulk collect into collection_name;

 

2 、将 execute immediate forall 语句结合起来一起使用进行批 DML, 语法如下:

   Forall index in lower..upper

        Execute  immediate ‘sql_statement ‘  using collection_name;

  例子:

       Forall I in  region_ids.first..region_ids.last

         Execute immediate ‘insert into region_tab value (:ip_ids,:ip_name)’ using region_ids(i),region_names(i);

 

3 、使用 fetch..bulk collect into... 从使用本地动态 SQL 打开的游标中进行批取值,语法如下:

       Open cursor_variable_name for dyn_sql_statement;

Fetch cursor variable_name bulk collect into collection_name;

例子:

Cursor cur_tuser is select tname form t_user order by id;

Begin

  open cur_tuser;

  fetch cur_tuser bulk collect into  tname;

使用动态语句,把以上代码改成如下:

Type rc is ref cursor;

Cur_tuser rc;

Begin

  Open cur_tuser form ‘select name form t_user’;

  Fetch cur_tuser bulk collect into region_names;

 

 

 

posted on 2006-09-30 16:38 有猫相伴的日子 阅读(1437) 评论(1)  编辑  收藏 所属分类: pl/sql

评论:
# re: oracle本地批绑定 2007-01-23 11:21 | laocai
哥们,太好了,已经抄下来了。  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航:
 
本站不再更新,欢迎光临 java开发技术网