本站不再更新,歡迎光臨 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 的語句組成的??梢允褂?/span> 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(? --?¨ò?????ààDí
          ???
          -- id number,
          ??
          --? name varchar2(200),
          ??
          --? sex varchar2(2)
          ?
          --? );
          ??
          --type user_tab is table of user_record index by binary_integer; --?¨ò?user_recordààDíμ?index-by±íààDí
          ??
          --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 ? --3?ê??ˉ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 的名稱??梢栽?/span> select into fetch into returning into 子句中使用 bulk collect ??梢栽?/span> 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 有貓相伴的日子 閱讀(1451) 評論(1)  編輯  收藏 所屬分類: pl/sql

          評論:
          # re: oracle本地批綁定 2007-01-23 11:21 | laocai
          哥們,太好了,已經抄下來了。  回復  更多評論
            
          本站不再更新,歡迎光臨 java開發技術網
          主站蜘蛛池模板: 闵行区| 松溪县| 巩义市| 扎赉特旗| 安多县| 海门市| 石门县| 青川县| 龙门县| 丹凤县| 吴旗县| 永仁县| 黔江区| 孟津县| 老河口市| 应城市| 北流市| 横峰县| 东乡县| 黑山县| 浪卡子县| 泸溪县| 辽中县| 西畴县| 东方市| 宜昌市| 靖边县| 德兴市| 城口县| 赤峰市| 中江县| 大埔区| 义马市| 千阳县| 隆德县| 西藏| 西青区| 土默特左旗| 松原市| 左云县| 安达市|