本文原發佈於簡書,地址爲: Oracle數據庫之FORALL與BULK COLLECT語句。
更多數據庫資訊請參看github: 數據庫知識彙總
當PL/SQL運行時引擎處理一塊代碼時,它使用PL/SQL引擎來執行過程化的代碼,而將SQL語句發送給SQL引擎來執行;
SQL引擎執行完畢後,將結果再返回給PL/SQL引擎。這種在PL/SQL引擎和SQL引擎之間的交互,稱爲上下文交換(context switch)。
每發生一次交換,就會帶來必定的額外開銷。git
這兩個語句在PL/SQL內部進行一種數組處理,BULK COLLECT提供對數據的高速檢索,FORALL可大大改進INSERT、UPDATE和DELETE操做的性能。Oracle數據庫使用這些語句大大減小了PL/SQL與SQL語句執行引擎的環境切換次數,從而使其性能有了顯著提升。github
若是你要插入5000條數據,通常狀況下,在pl/sql中用for循環,循環插入5000次,而用forall一次就能夠插入5000條,提升了性能和速度。
使用FORALL,能夠將多個DML批量發送給SQL引擎來執行,最大限度地減小上下文交互所帶來的開銷。sql
FORALL index_name IN { lower_bound .. upper_bound | INDICES OF collection_name [ BETWEEN lower_bound AND upper_bound ] | VALUES OF index_collection } [ SAVE EXCEPTIONS ] dml_statement;
說明:數據庫
見sqlscripts/forall-bulkcollect包下的sql腳本事例數組
使用FORALL時,應該遵循以下規則:服務器
--error statement --1.insert into test2 values dr_table(i);dbms_output.put_line(i);不正確,找不到i,由於forall中只能使用單條語句能夠引用索引變量 --2.insert into test2 values(dr_table(i).id,dr_table(i).name);集合的field不能夠在forall中使用,必須是總體使用 --3.insert into test2 values dr_table(i+1);錯誤,不能夠對索引變量進行運算 --4.insert into test2 values(dr_table(i));報沒有足夠的值錯誤,此處外面不能夠加括號,當有多個字段的時候,單個字段能夠加括號
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 -- 使用BULK COLLECT將所得的結果集一次性綁定到記錄變量emp_tab中 SELECT EMPNO, ENAME, HIREDATE BULK COLLECT INTO EMP_TAB FROM EMP; FOR I IN EMP_TAB.FIRST .. EMP_TAB.LAST LOOP DBMS_OUTPUT.PUT_LINE('當前記錄: ' || EMP_TAB(I) .EMPNO || CHR(9) || EMP_TAB(I) .ENAME || CHR(9) || EMP_TAB(I).HIREDATE); END LOOP; END;
說明:使用BULK COLLECT一次便可提取全部行並綁定到記錄變量,這就是所謂的批量綁定。網絡
在遊標中可使用BLUK COLLECT一次取出一個數據集合,比用遊標單條取數據效率高,尤爲是在網絡不大好的狀況下。oop
語法:性能
FETCH ... BULK COLLECT INTO ...[LIMIT row_number];
注意:fetch
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; -- 定義了一個變量來做爲limit的值 V_LIMIT PLS_INTEGER := 5; -- 定義變量來記錄FETCH次數 V_COUNTER PLS_INTEGER := 0; BEGIN OPEN EMP_CUR; LOOP -- fetch時使用了BULK COLLECT子句 FETCH EMP_CUR BULK COLLECT INTO EMP_TAB 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('當前記錄: ' || 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('總共獲取次數爲:' || V_COUNTER); END;
BULK COLLECT除了與SELECT,FETCH進行批量綁定以外,還能夠與INSERT,DELETE,UPDATE語句結合使用。
當與這幾個DML語句結合時,須要使用RETURNING子句來實現批量綁定。
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 DELETE FROM EMP WHERE DEPTNO = 20 RETURNING EMPNO, ENAME, HIREDATE -- 使用returning 返回這幾個列 BULK COLLECT INTO EMP_TAB; -- 將返回的列的數據批量插入到集合變量 DBMS_OUTPUT.PUT_LINE('刪除 ' || SQL%ROWCOUNT || ' 行記錄'); COMMIT; IF EMP_TAB.COUNT > 0 THEN -- 當集合變量不爲空時,輸出全部被刪除的元素 FOR I IN EMP_TAB.FIRST .. EMP_TAB.LAST LOOP DBMS_OUTPUT.PUT_LINE('當前記錄:' || EMP_TAB(I) .EMPNO || CHR(9) || EMP_TAB(I) .ENAME || CHR(9) || EMP_TAB(I) .HIREDATE || ' 已被刪除'); END LOOP; END IF; END;
FORALL與BULK COLLECT是實現批量SQL的兩個重要方式,咱們能夠將其結合使用以提升性能.
-- create tb_emp_test CREATE TABLE tb_emp_test AS SELECT empno, ename, hiredate FROM EMP_TEST WHERE 1 = 0; DECLARE -- declare cursor CURSOR EMP_CUR IS SELECT EMPNO, ENAME, HIREDATE FROM EMP_TEST; -- 基於遊標的嵌套表類型 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 FROM EMP_TEST WHERE SAL > 1000; -- 使用FORALL語句將變量中的數據插入到表tb_emp FORALL I IN 1 .. EMP_TAB.COUNT INSERT INTO (SELECT EMPNO, ENAME, HIREDATE FROM TB_EMP_TEST) VALUES EMP_TAB (I); COMMIT; DBMS_OUTPUT.PUT_LINE('總共向 tb_emp 表中插入記錄數: ' || EMP_TAB.COUNT); END;
Oracle9i以前有binary_integer類型,和11g中引入的pls_integer數值範圍相同:-2147483647~+2147483647,但pls_integer有更高的性能。二者性能均優於number類型。 Oracle中也引入了simple_integer類型,不過不能包含null值,範圍:-2147483648~2147483647,性能優於pls_integer。
1. 加了"index by binary_integer "後,numbers類型的下標就是自增加,numbers類型在插入元素時,不須要初始化,不須要每次extend增長一個空間。 2. 若是沒有這句話"index by binary_integer",那就得要顯示對初始化,且每插入一個元素到numbers類型的table中時,都須要先extend。