【DB筆試面試465】如何使用批量動態SQL(FORALL及BULK子句的使用)?
- 2019 年 10 月 11 日
- 筆記
題目部分
如何使用批量動態SQL(FORALL及BULK子句的使用)?
答案部分
批量動態SQL即在動態SQL中使用BULK子句,或使用游標變量時在FETCH中使用BULK,或在FORALL子句中使用BULK子句來實現。
如果一個循環內執行了INSERT、DELETE或UPDATE等語句引用了集合元素,那麼可以將其移動到一個FORALL子句中。如果SELECT INTO、FETCH INTO或RETURNING INTO子句引用了一個集合,那麼應該使用BULK COLLECT子句進行合併,從而來提高程序的性能。
1、動態SQL中使用BULK子句的語法
EXECUTE IMMEDIATE dynamic_string --dynamic_string用於存放動態SQL字符串 [BULK COLLECT INTO define_variable[,define_variable...]] --存放查詢結果的集合變量 [USING bind_argument[,argument...]] --使用參數傳遞給動態SQL [{RETURNING | RETURN} --返回子句 BULK COLLECT INTO return_variable[,return_variable...]]; --存放返回結果的集合變量
使用BULK COLLECT INTO子句處理動態SQL中的多行查詢可以加快處理速度,從而提高應用程序的性能。當使用BULK子句時,集合類型可以是PL/SQL所支持的索引表、嵌套表和VARRY,但集合元素必須使用SQL數據類型。常用的三種語句支持BULK子句,分別為EXECUTE IMMEDIATE,FETCH和FORALL。
2、使用EXECUTE IMMEDIATE結合BULK子句處理DML語句返回子句
下面的例子,首先定義了兩個索引表類型以及其變量,接下來使用動態SQL語句來更新T_20170104_LHR的薪水,使用EXECUTE IMMEDIATE配合BULK COLLECT INTO來處理結果集。
CREATE TABLE T_20170104_LHR AS SELECT EMPNO,ENAME,SAL,DEPTNO FROM SCOTT.EMP;--準備基礎表 DECLARE TYPE ENAME_TABLE_TYPE IS TABLE OF T_20170104_LHR.ENAME%TYPE INDEX BY BINARY_INTEGER; --定義類型用於存放結果集 TYPE SAL_TABLE_TYPE IS TABLE OF T_20170104_LHR.SAL%TYPE INDEX BY BINARY_INTEGER; ENAME_TABLE ENAME_TABLE_TYPE; SAL_TABLE SAL_TABLE_TYPE; SQL_STAT VARCHAR2(120); V_PERCENT NUMBER := 1; V_DNO NUMBER := 10; BEGIN SQL_STAT := 'UPDATE T_20170104_LHR SET SAL = SAL * (1 + :PERCENT / 100)' --動態DML語句 || ' WHERE DEPTNO = :DNO' || ' RETURNING ENAME, SAL INTO :NAME, :SALARY'; --使用了RETURNING子句,有返回值 EXECUTE IMMEDIATE SQL_STAT USING V_PERCENT, V_DNO --執行動態SQL語句 RETURNING BULK COLLECT INTO ENAME_TABLE, SAL_TABLE; --使用BULK COLLECT INTO到集合變量 FOR I IN 1 .. ENAME_TABLE.COUNT --使用FOR循環讀取集合變量的結果 LOOP DBMS_OUTPUT.PUT_LINE('Employee ' || ENAME_TABLE(I) || ' Salary is: ' || SAL_TABLE(I)); END LOOP; END; /
運行以上程序輸出結果如下所示:
Employee CLARK Salary is: 2474.5 Employee KING Salary is: 5050 Employee MILLER Salary is: 1313
3、使用EXECUTE IMMEDIATE結合BULK子句處理多行查詢
在下面示例中,與前一個示例相同,只不過其動態SQL由查詢語句組成,且返回多個結果集,同樣使用了BULK COLLECT INTO來傳遞結果。
DECLARE TYPE ENAME_TABLE_TYPE IS TABLE OF t_20170104_lhr.ENAME%TYPE INDEX BY BINARY_INTEGER; --定義類型用於存放結果集 TYPE SAL_TABLE_TYPE IS TABLE OF t_20170104_lhr.SAL%TYPE INDEX BY BINARY_INTEGER; ENAME_TABLE ENAME_TABLE_TYPE; SAL_TABLE SAL_TABLE_TYPE; SQL_STAT VARCHAR2(100); BEGIN SQL_STAT := 'SELECT ENAME,SAL FROM T_20170104_LHR WHERE DEPTNO = :DNO'; --動態DQL語句,未使用RETURNING子句 EXECUTE IMMEDIATE SQL_STAT BULK COLLECT INTO ENAME_TABLE, SAL_TABLE USING 10; --使用BULK COLLECT INTO FOR I IN 1 .. ENAME_TABLE.COUNT LOOP DBMS_OUTPUT.PUT_LINE('Employee ' || ENAME_TABLE(I) || ' Salary is: ' ||SAL_TABLE(I)); END LOOP; COMMIT; END; / 運行以上程序輸出結果如下所示: Employee CLARK Salary is: 2450 Employee KING Salary is: 5000 Employee MILLER Salary is: 1300
4、使用FETCH子句結合BULK子句處理多行結果集
下面的示例中首先定義了游標類型,游標變量以及複合類型,複合變量,接下來從動態SQL中OPEN游標,然後使用FETCH將結果存放到複合變量中。即使用OPEN,FETCH代替了EXECUTE IMMEDIATE來完成動態SQL的執行。
DECLARE TYPE EMPCURTYPE IS REF CURSOR; --定義游標類型及游標變量 EMP_CV EMPCURTYPE; TYPE ENAME_TABLE_TYPE IS TABLE OF t_20170104_lhr.ENAME%TYPE INDEX BY BINARY_INTEGER; --定義結果集類型及變量 ENAME_TABLE ENAME_TABLE_TYPE; SQL_STAT VARCHAR2(120); BEGIN SQL_STAT := 'SELECT ENAME FROM T_20170104_LHR WHERE DEPTNO = :DNO'; --動態SQL字符串 OPEN EMP_CV FOR SQL_STAT USING 10; --從動態SQL中打開游標 FETCH EMP_CV BULK COLLECT INTO ENAME_TABLE;--使用BULK COLLECT INTO提取結果集 FOR I IN 1 .. ENAME_TABLE.COUNT LOOP DBMS_OUTPUT.PUT_LINE('Employee Name is ' || ENAME_TABLE(I)); END LOOP; CLOSE EMP_CV; END; /
運行以上程序輸出結果如下所示:
Employee Name is CLARK Employee Name is KING Employee Name is MILLER
5、在FORALL子句中使用BULK子句
下面是FORALL子句的語法:
FORALL index IN lower bound..upper bound --FORALL循環計數 EXECUTE IMMEDIATE dynamic_string --結合EXECUTE IMMEDIATE來執行動態SQL語句 USING bind_argument | bind_argument(index) --綁定輸入參數 [bind_argument | bind_argument(index)]... [{RETURNING | RETURN} BULK COLLECT INTO bind_argument[,bind_argument...]]; --綁定返回結果集
FORALL子句允許為動態SQL輸入變量,但FORALL子句僅支持DML(INSERT、DELETE、UPDATE)語句,不支持動態的SELECT語句。
下面的示例中,首先聲明了兩個複合類型以及複合變量,接下來為複合變量ENAME_TABLE賦值,以形成動態SQL語句。緊接着使用FORALL子句結合EXECUTE IMMEDIATE 來提取結果集。
DECLARE --定義複合類型及變量 TYPE ENAME_TABLE_TYPE IS TABLE OF T_20170104_LHR.ENAME%TYPE; TYPE SAL_TABLE_TYPE IS TABLE OF T_20170104_LHR.SAL%TYPE; ENAME_TABLE ENAME_TABLE_TYPE; SAL_TABLE SAL_TABLE_TYPE; SQL_STAT VARCHAR2(100); BEGIN ENAME_TABLE := ENAME_TABLE_TYPE('BLAKE', 'FORD', 'MILLER'); --為複合類型賦值 SQL_STAT := 'UPDATE T_20170104_LHR SET SAL = SAL * 1.1 WHERE ENAME = :1' --定義動態SQL語句 || ' RETURNING SAL INTO :2'; FORALL I IN 1 .. ENAME_TABLE.COUNT --為FORALL設定起始值 EXECUTE IMMEDIATE SQL_STAT USING ENAME_TABLE(I) --使用EXECUTE IMMEDIATE 結合RETURNING BULK COLLECT INTO獲取結果集 RETURNING BULK COLLECT INTO SAL_TABLE ; FOR J IN 1 .. ENAME_TABLE.COUNT LOOP DBMS_OUTPUT.PUT_LINE('The new salary is ' || SAL_TABLE(J) || ' for ' ||ENAME_TABLE(J)); END LOOP; COMMIT; END; /
運行以上程序輸出結果如下所示:
The new salary is 3135 for BLAKE The new salary is 3300 for FORD The new salary is 1430 for MILLER
本文選自《Oracle程序員面試筆試寶典》,作者:李華榮。
About Me:小麥苗
● 本文作者:小麥苗,只專註於數據庫的技術,更注重技術的運用
● 作者博客地址:http://blog.itpub.net/26736162/abstract/1/
● 本系列題目來源於作者的學習筆記,部分整理自網絡,若有侵權或不當之處還請諒解
● 版權所有,歡迎分享本文,轉載請保留出處
● 題目解答若有不當之處,還望各位朋友批評指正,共同進步