批量SQL之 BULK COLLECT 子句
BULK COLLECT 子句會批量檢索結果,即一次性將結果集繫結到一個集合變數中,並從SQL引擎傳送到PL/SQL引擎。通常可以在SELECT INTO、
FETCH INTO以及RETURNING INTO子句中使用BULK COLLECT。本文將逐一描述BULK COLLECT在這幾種情形下的用法。
有關FORALL語句的用法請參考:批量SQL之 FORALL 語句
一、BULK COLLECT批量繫結的示例
--下面的示例中使用了BULK COLLECT將得到的結果集繫結到記錄變數中 DECLARE TYPE emp_rec_type IS RECORD --宣告記錄型別 ( empno emp.empno%TYPE ,ename emp.ename%TYPE ,hiredate emp.hiredate%TYPE ); TYPE nested_emp_type IS TABLE OF emp_rec_type; --宣告記錄型別變數 emp_tab nested_emp_type; BEGIN SELECT empno, ename, hiredate BULK COLLECT INTO emp_tab --使用BULK COLLECT 將所得的結果集一次性繫結到記錄變數emp_tab中 FROM emp; FOR i IN emp_tab.FIRST .. emp_tab.LAST LOOP DBMS_OUTPUT.put_line('Current record is '||emp_tab(i).empno||chr(9)||emp_tab(i).ename||chr(9)||emp_tab(i).hiredate); END LOOP; END; --上面的例子可以通過FOR 迴圈和普通的SELECT INTO來實現,那兩者之間的差異呢? --差異是FOR迴圈的SELECT INTO逐行提取並繫結到記錄變數,而BULK COLLECT則一次即可提取所有行並繫結到記錄變數。即謂批量繫結。
二、使用LIMIT限制FETCH資料量
在使用BULK COLLECT 子句時,對於集合型別,如巢狀表,聯合陣列等會自動對其進行初始化以及擴充套件(如下示例)。因此如果使用BULK
COLLECT子句操作集合,則無需對集合進行初始化以及擴充套件。由於BULK COLLECT的批量特性,如果資料量較大,而集合在此時又自動擴充套件,為避
免過大的資料集造成效能下降,因此使用limit子句來限制一次提取的資料量。limit子句只允許出現在fetch操作語句的批量中。
用法:
FETCH ... BULK COLLECT INTO ... [LIMIT rows]
DECLARE CURSOR emp_cur IS SELECT empno, ename, hiredate FROM emp; TYPE emp_rec_type IS RECORD ( empno emp.empno%TYPE ,ename emp.ename%TYPE ,hiredate emp.hiredate%TYPE ); TYPE nested_emp_type IS TABLE OF emp_rec_type; -->定義了基於記錄的巢狀表 emp_tab nested_emp_type; -->定義集合變數,此時未初始化 v_limit PLS_INTEGER := 5; -->定義了一個變數來作為limit的值 v_counter PLS_INTEGER := 0; BEGIN OPEN emp_cur; LOOP FETCH emp_cur BULK COLLECT INTO emp_tab -->fetch時使用了BULK COLLECT子句 LIMIT v_limit; -->使用limit子句限制提取資料量 EXIT WHEN emp_tab.COUNT = 0; -->注意此時遊標退出使用了emp_tab.COUNT,而不是emp_cur%notfound v_counter := v_counter + 1; -->記錄使用LIMIT之後fetch的次數 FOR i IN emp_tab.FIRST .. emp_tab.LAST LOOP DBMS_OUTPUT.put_line( 'Current record is '||emp_tab(i).empno||CHR(9)||emp_tab(i).ename||CHR(9)||emp_tab(i).hiredate); END LOOP; END LOOP; CLOSE emp_cur; DBMS_OUTPUT.put_line( 'The v_counter is ' || v_counter ); END;
三、RETURNING 子句的批量繫結
BULK COLLECT除了與SELECT,FETCH進行批量繫結之外,還可以與INSERT,DELETE,UPDATE語句結合使用。當與這幾個DML語句結合時,我們
需要使用RETURNING子句來實現批量繫結。
--下面示例中從表emp中刪除所有deptno=20的記錄 DECLARE TYPE emp_rec_type IS RECORD ( empno emp.empno%TYPE ,ename emp.ename%TYPE ,hiredate emp.hiredate%TYPE ); TYPE nested_emp_type IS TABLE OF emp_rec_type; emp_tab nested_emp_type; -- v_limit PLS_INTEGER := 3; -- v_counter PLS_INTEGER := 0; BEGIN DELETE FROM emp WHERE deptno = 20 RETURNING empno, ename, hiredate -->使用returning 返回這幾個列 BULK COLLECT INTO emp_tab; -->將前面返回的列的資料批量插入到集合變數 DBMS_OUTPUT.put_line( 'Deleted ' || SQL%ROWCOUNT || ' rows.' ); COMMIT; IF emp_tab.COUNT > 0 THEN -->當集合變數不為空時,輸出所有被刪除的元素 FOR i IN emp_tab.FIRST .. emp_tab.LAST LOOP DBMS_OUTPUT. put_line( 'Current record ' || emp_tab( i ).empno || CHR( 9 ) || emp_tab( i ).ename || CHR( 9 ) || emp_tab( i ).hiredate || ' has been deleted' ); END LOOP; END IF; END;
四、FORALL與BULK COLLECT 綜合運用
FORALL與BULK COLLECT是實現批量SQL的兩個重要方式,我們可以將其結合使用以提高效能。下面的示例即是兩者的總和運用。
DROP TABLE tb_emp;
CREATE TABLE tb_emp AS -->建立表tb_emp
SELECT empno, ename, hiredate
FROM emp
WHERE 1 = 2;
DECLARE
CURSOR emp_cur IS -->宣告遊標
SELECT empno, ename, hiredate FROM emp;
TYPE nested_emp_type IS TABLE OF emp_cur%ROWTYPE; -->基於遊標的巢狀表型別
emp_tab nested_emp_type; -->宣告巢狀變數
BEGIN
SELECT empno, ename, hiredate
BULK COLLECT INTO emp_tab -->BULK COLLECT批量提取資料
FROM emp
WHERE sal > 1000;
FORALL i IN 1 .. emp_tab.COUNT -->使用FORALL語句將變數中的資料插入到表tb_emp
INSERT INTO (SELECT empno, ename, hiredate FROM tb_emp)
VALUES emp_tab( i );
COMMIT;
DBMS_OUTPUT.put_line( 'The total ' || emp_tab.COUNT || ' rows has been inserted to tb_emp' );
END;
五、BULK COLLECT的限制
1、不能對使用字串型別作鍵的關聯陣列使用BULK COLLECT 子句。
2、只能在伺服器端的程式中使用BULK COLLECT,如果在客戶端使用,就會產生一個不支援這個特性的錯誤。
3、BULK COLLECT INTO 的目標物件必須是集合型別。
4、複合目標(如物件型別)不能在RETURNING INTO 子句中使用。
5、如果有多個隱式的資料型別轉換的情況存在,多重複合目標就不能在BULK COLLECT INTO 子句中使用。
6、如果有一個隱式的資料型別轉換,複合目標的集合(如物件型別集合)就不能用於BULK COLLECTINTO 子句中。
六、更多參考