FORALL和BULK COLLECT
參考:http://www.cnblogs.com/hellokitty1/p/4584333.html
用戶在通過PLSQL編寫程序時,PLSQL通常會在操作上進行交互,當用戶通過PLSQL執行一條更新語句時,SQL會將更新後的數據返回給PLSQL,這樣用戶才可以在PLSQL之中取得更新後的數據,但是如果在PLSQL之中要進行大量的數據操作時,這種方式就會使程序執行性能大大降低。
例如:通過PLSQL執行多行數據更新
1 DECLARE 2 TYPE emp_array_type IS VARRAY(10) OF emp.empno%TYPE ; --定義可變數組 3 4emp_array emp_array_type := emp_array_type(7369 , 7499 , 7521 , 7566) ; --初始化可變數組 5 BEGIN 6 FOR x IN emp_array.first .. emp_array.last LOOP 7 UPDATE lxemp SET sal = sal+1000 WHERE empno = emp_array(x) ; --更新數據 8 END LOOP ; 9 END ;
如果按照上面的做法,那麽集合中有多少個元素就要執行多少次更新操作。
上面的這個程序一共向數據庫發送了5次更新操作。假設要更新的內容很多,這樣的方式很明顯是浪費時間和性能。
最好的方式是一次性向數據庫發出N多條的更新操作,采用批處理的方式。
FORALL是將所有需要更新的操作一次性發送給數據庫。
1、FORALL語句:
1 FORALL index_name IN 2 { lower_bound .. upper_bound 3 | INDICES OF collection_name [ BETWEEN lower_bound AND upper_bound ] 4 | VALUES OF index_collection 5 } 6 [ SAVE EXCEPTIONS ] dml_statement;
說明:
index_name : 一個無需聲明的標識符,作為集合的下標使用
lower_bound .. upper_bound : 數字表達式,用來指定一組連續有效的索引數字下線和上限,該表達式只解析一次。
INDICES OF collection_name : 用於執行稀疏數組的實際下標。跳過沒有賦值的元素,例如被DELETE的元素,NULL值也算。
BETWEEN lower_bound AND upper_bound : 指定一個範圍之內的數據
VALUES OF index_collection : 把該集合中的值作為下標,且該集合值的類型只能是PLS_INTEGER或BINARY_INTEGER。
SAVE EXCEPTIONS : 可選關鍵字,表示即使一些DML語句失敗,知道FORALL LOOP 執行完畢才拋出異常。可以使用SQL%BULK_EXCEPTION查看異常。
dml_statement : 靜態語句,例如UPDATE 或 DELETE ,或者動態(EXECUTE IMMEDIATE)DML語句。
2、使用FORALL
示例1:使用FORALL批量插入、修改、刪除數據
新建一張臨時操作的表。
1 CREATE TABLE mylx ( 2 mid NUMBER(10) , 3 mname VARCHAR2 (20) , 4 msal NUMBER(10) 5 ) ;
1 --insert 2 DECLARE 3 TYPE emp_table_type IS TABLE OF mylx%ROWTYPE ; --定義一個嵌套表類型 4 emp_table emp_table_type := emp_table_type() ; --初始化嵌套表變量 5 BEGIN 6 FOR x IN 1 .. 5 LOOP 7 emp_table.extend() ; --為emp_table嵌套變量擴充容量 8 emp_table(x).mid := x ; 9 emp_table(x).mname := ‘name‘ || x ; 10 emp_table(x).msal := x * 10 ; 11 END LOOP ; 12 FORALL x IN emp_table.first .. emp_table.last 13 INSERT INTO mylx VALUES emp_table(x) ; --批量插入 14 END ;
1 --UPDATE 2 DECLARE 3 TYPE emp_array_type IS VARRAY(10) OF emp.empno%TYPE ; --定義可變數組 4 emp_array emp_array_type := emp_array_type(1 , 2 , 3 , 4 , 5) ; --初始化可變數組 5 BEGIN 6 FORALL x IN emp_array.first .. emp_array.last 7 UPDATE mylx SET msal= 9000 WHERE mid = emp_array(x) ; 8 END ;
1 --delete 2 DECLARE 3 TYPE emp_array_type IS VARRAY(10) OF emp.empno%TYPE ; --定義可變數組 4 emp_array emp_array_type := emp_array_type(1 , 2 ) ; --初始化可變數組 5 BEGIN 6 FORALL x IN emp_array.first .. emp_array.last 7 DELETE FROM mylx WHERE mid = emp_array(x) ; 8 END ;
--語法1:
FORALL 下標變量(只能當作下標被引用) IN 下限..上限
sql 語句; --只允許一條 sql 語句
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
示例2:在FORALL 中使用 INDICES OF子句
1 --INDICES OF 2 DECLARE 3 TYPE num_insex_type IS TABLE OF mylx%ROWTYPE INDEX BY PLS_INTEGER ; 4 num_index num_insex_type ; 5 6 BEGIN 7 FOR x IN 1 .. 5 LOOP 8 num_index(x).mid := x ; 9 num_index(x).mname := ‘zhang‘ || x ; 10 num_index(x).msal := x*100 ; 11 END LOOP ; 12 13 num_index.delete(2) ; 14 num_index.delete(4) ; 15 16 FORALL x IN INDICES OF num_index 17 INSERT INTO mylx VALUES num_index(x) ; 18 END ;
在使用INDICES OF 時 ,如果集合中某個值為NULL 或者 被DELETE了 ,那麽它就會跳過那個值繼續執行,知道執行完畢。
--語法2:
FORALL 下標變量 IN INDICES OF(跳過沒有賦值的元素,例如被 DELETE 的元素,NULL 也算值) 集合
[BETWEEN 下限 AND 上限]
sql 語句;
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
示例3 : 在FORALL 中使用 VALUES OF 子句
1 --VALUES OF 2 DECLARE 3 TYPE px_index_type IS TABLE OF PLS_INTEGER ; -- 創建一個嵌套表類型 (集合值的類型只能是 PLS_INTEGER 或 BINARY_INTEGER) 4 px_index px_index_type ; 5 TYPE mupx_index_type IS TABLE OF mylx%ROWTYPE INDEX BY BINARY_INTEGER ; -- 創建一個嵌套表類型 6 mupx_index mupx_index_type ; 7 BEGIN 8 px_index := px_index_type (1 , 3 , 5 ) ; --實例化 px_index_type 9 10 FOR x IN 1 .. 5 LOOP 11 mupx_index(x).mid := x ; --實例化 mupx_index_type 12 mupx_index(x).mname := ‘jie‘ || x ; 13 mupx_index(x).msal := x*100 ; 14 END LOOP ; 15 16 17 FORALL x IN VALUES OF px_index 18 UPDATE mylx SET msal = 8888 WHERE mid = mupx_index(x).mid ; 19 END ;
使用的VALUES OF 子句的時候 , px_index_type 集合的值是作為下標使用的。mupx_index_type 是根據 px_index_type 這個集合的值進行操作的 。
--語法3:
FORALL 下標變量 IN VALUES OF 集合(把該集合中的值當作下標,且該集合值的類型只能是 PLS_INTEGER BINARY_INTEGER)
sql 語句;
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2、FORALL註意事項:
a、FORALL語句執行體,必須是一個單獨的DML語句,比如INSERT 、 UPDATE 、 DELETE。
b、不要顯示定義index_row,他被PLSQL引擎隱式的定義為PLS_INTEGER類型,並且它的作用域僅僅是FORALL。
c、這個DML語句必須與一個集合的元素相關,並且使用FORALL中的index_row來索引。註意不要因為index_row導致集合下標越界。
d、lower_bound和upper_bound之間是按照步進 1 來遞增的。
e、在sql_statement中,不能單獨地引用集合中的元素,只能批量地使用集合。
f、在sql_statement中使用的集合,下標不能使用表達式。
4、BULK COLLECT介紹
BULK COLLECT子句會批量檢索結果,即一次性將結果集綁定到一個集合變量中。並從sql引擎發送到PLSQL。通常可以在SELECT INOT 、 FETCH INTO 以及 RETURNING INTO 中使用BULK COLLECT。
5、BULK COLLECT 的使用
5.1 在SELECT INTO 中使用BULK COLLECT
實例:
1 DECLARE 2 TYPE emp_table_type IS TABLE OF mylx%ROWTYPE ; 3 emp_table emp_table_type ; 4 BEGIN 5 SELECT * BULK COLLECT INTO emp_table FROM mylx ; --在SELECT INTO 中使用BULK COLLECT 6 7 FOR x IN emp_table.first .. emp_table.last LOOP 8 dbms_output.put_line (‘編號 : ‘ || emp_table(x).mid || ‘ , 姓名 : ‘ || emp_table(x).mname || ‘ , 工資 :‘ || emp_table(x).msal) ; 9 END LOOP ; 10 END ;
5.2在FETCH INTO 中使用BULK COLLECT
在遊標中可以使用BLUK COLLECT一次取出一個數據集合,比用遊標單條取數據效率高,尤其是在網絡不大好的情況下。
語法:
FETCH ... BULK COLLECT INTO ...[LIMIT row_number];
在使用BULK COLLECT子句時,對於集合類型會自動對其進行初始化以及擴展。因此如果使用BULK COLLECT子句操作集合,則無需對集合進行初始化以及擴展。由於BULK COLLECT的批量特性,如果數據量較大,而集合在此時又自動擴展,為避免過大的數據集造成性能下降,因此可以使用LIMIT子句來限制一次提取的數據量。LIMIT子句只允許出現在FETCH操作語句的批量中。
1 DECLARE 2 CURSOR mylx_cur -- 創建遊標 3 IS 4 SELECT * FROM mylx ; 5 6 TYPE mylx_table_type IS TABLE OF mylx%ROWTYPE ; -- 創建一個嵌套表類型 7 mylx_table mylx_table_type ; 8 BEGIN 9 OPEN mylx_cur ; 10 FETCH mylx_cur BULK COLLECT INTO mylx_table ; -- 在FETCH INTO 中使用 BULK COLLECT 11 12 FOR x IN mylx_table.first .. mylx_table.last LOOP -- 遍歷所有雇員 13 dbms_output.put_line (‘編號 : ‘ || mylx_table(x).mid || ‘ , 姓名 : ‘ || mylx_table(x).mname || ‘ , 工資 :‘ || mylx_table(x).msal) ; 14 END LOOP ; 15 /* 16 FORALL x IN mylx_table.first .. mylx_table.last --批量修改所有雇員的工資 17 UPDATE mylx SET msal = 10000 WHERE mid = mylx_table(x).mid ;*/ 18 CLOSE mylx_cur ; 19 END ;
5.3 在RETURNING INTO 中使用 BULK COLLECT
BULK COLLECT除了與SELECT,FETCH進行批量綁定之外,還可以與INSERT,DELETE,UPDATE語句結合使用。當與這幾個DML語句結合時,需要使用RETURNING子句來實現批量綁定。
1 DECLARE 2 TYPE mylx_table_type IS TABLE OF mylx%ROWTYPE ; 3 mylx_table mylx_table_type ; 4 BEGIN 5 DELETE FROM mylx WHERE msal = 10000 -- 刪除mylx表 工資為10000 的員工 6 RETURNING mid , mname , msal -- 使用RETURNING 返回 這幾個列 7 BULK COLLECT INTO mylx_table ; -- 把返回的數據INTO到mylx_table 8 9 IF mylx_table.COUNT > 0 THEN 10 FOR x IN mylx_table.first .. mylx_table.last LOOP 11 dbms_output.put_line (‘編號 : ‘ || mylx_table(x).mid || ‘ , 姓名 : ‘ || mylx_table(x).mname || ‘ , 工資 :‘ || mylx_table(x).msal || ‘被刪除‘) ; 12 END LOOP ; 13 END IF ; 14 END ;
6 、 BULK COLLECT的註意事項
- q
7、 BULK COLLECT 和 FORALL 總合應用
1 DECLARE 2 CURSOR mylx_cur 3 IS 4 SELECT * FROM mylx ; 5 6 TYPE mylx_table_type IS TABLE OF mylx%ROWTYPE ; 7 mylx_table mylx_table_type ; 8 BEGIN 9 OPEN mylx_cur ; 10 FETCH mylx_cur BULK COLLECT INTO mylx_table ; -- 取出全部的數據到集合中 11 12 FORALL x IN mylx_table.first .. mylx_table.last -- 使用FORALL 進行批量刪除 13 DELETE FROM mylx WHERE mid = mylx_table(x).mid ; 14 CLOSE mylx_cur ; 15 END ;
FORALL和BULK COLLECT