【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/

● 本系列題目來源於作者的學習筆記,部分整理自網絡,若有侵權或不當之處還請諒解

● 版權所有,歡迎分享本文,轉載請保留出處

● 題目解答若有不當之處,還望各位朋友批評指正,共同進步