【DB筆試面試466】存儲過程或函數如何返回集合類型?

  • 2019 年 10 月 11 日
  • 筆記

題目部分

存儲過程或函數如何返回集合類型?

答案部分

TABLE()函數可接受查詢語句或游標作為輸入參數,並可輸出多行數據,稱為表函數。所以,存儲過程或函數返回集合類型主要採用的是表函數和PIPELINED函數(管道化表函數)及數組結合的方式。當然,也可以採用存儲過程返回系統游標SYS_REFCURSOR或自定義游標的方式。

下面先看一個使用表函數的最簡單的例子:

CREATE TABLE TEST (ID VARCHAR2(20));  INSERT INTO TEST VALUES('1');  COMMIT;  EXPLAIN PLAN FOR SELECT * FROM TEST;  SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

結果如下:

PLAN_TABLE_OUTPUT  --------------------------------------------------------------------------------  Plan hash value: 1357081020  --------------------------------------------------------------------------  | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |  --------------------------------------------------------------------------  |   0 | SELECT STATEMENT  |      |     1 |    12 |     2   (0)| 00:00:01 |  |   1 |  TABLE ACCESS FULL| TEST |     1 |    12 |     2   (0)| 00:00:01 |  --------------------------------------------------------------------------  Note  -----  PLAN_TABLE_OUTPUT  --------------------------------------------------------------------------------     - dynamic sampling used for this statement (level=2)

可以看到,一個簡單的表函數可以返回多行數據。下面再看一個TABLE()結合PIPELINED函數的例子:

CREATE OR REPLACE TYPE T_TEST AS OBJECT  (    ID      INTEGER,    IN_DATE DATE,    NAME    VARCHAR2(60)  );  --創建TYPE  CREATE OR REPLACE TYPE T_TEST_TABLE AS TABLE OF T_TEST;  --創建函數  CREATE OR REPLACE FUNCTION F_TEST_PIPE(N IN NUMBER DEFAULT NULL)    RETURN T_TEST_TABLE    PIPELINED AS    V_TEST T_TEST_TABLE := T_TEST_TABLE();  BEGIN    FOR I IN 1 .. NVL(N, 100) LOOP      PIPE ROW(T_TEST(I, SYSDATE, 'test' || I));    END LOOP;    RETURN;  END F_TEST_PIPE;  /  --查詢  SELECT * FROM TABLE(F_TEST_PIPE(5));  SELECT * FROM THE(SELECT F_TEST_PIPE(5) FROM DUAL);  SYS@lhrdb> SELECT * FROM TABLE(F_TEST_PIPE(5));          ID IN_DATE             NAME  ---------- ------------------- --------           1 2017-01-05 11:42:50 test1           2 2017-01-05 11:42:50 test2           3 2017-01-05 11:42:50 test3           4 2017-01-05 11:42:50 test4           5 2017-01-05 11:42:50 test5  下面給出TABLE()結合數組的例子:  CREATE OR REPLACE TYPE T_TEST AS OBJECT  (    ID      INTEGER,    IN_DATE DATE,    NAME    VARCHAR2(60)  )  ;  CREATE OR REPLACE TYPE T_TEST_TABLE AS TABLE OF T_TEST;  CREATE OR REPLACE FUNCTION F_TEST_ARRAY(N IN NUMBER DEFAULT NULL)    RETURN T_TEST_TABLE AS    V_TEST T_TEST_TABLE := T_TEST_TABLE();  BEGIN    FOR I IN 1 .. NVL(N, 100) LOOP      V_TEST.EXTEND();      V_TEST(V_TEST.COUNT) := T_TEST(I, SYSDATE, 'TEST' || I);    END LOOP;    RETURN V_TEST;  END F_TEST_ARRAY;  /  SELECT * FROM TABLE(F_TEST_ARRAY(5));  SELECT * FROM THE(SELECT F_TEST_ARRAY(5) FROM DUAL);  SYS@lhrdb> SELECT * FROM TABLE(F_TEST_ARRAY(5));          ID IN_DATE             NAME  ---------- ------------------- ----------------------           1 2017-01-05 11:48:50 TEST1           2 2017-01-05 11:48:50 TEST2           3 2017-01-05 11:48:50 TEST3           4 2017-01-05 11:48:50 TEST4           5 2017-01-05 11:48:50 TEST5

下面來看使用存儲過程如何返回遊標。下面的例子是返回一個系統游標SYS_REFCURSOR:

CREATE OR REPLACE PROCEDURE P_CURSOR_TLHR(CUR_ARG OUT SYS_REFCURSOR) AS  BEGIN    OPEN CUR_ARG FOR      SELECT * FROM USER_TABLES WHERE ROWNUM < 5;  END;  --調用測試  DECLARE      VARCURSOR SYS_REFCURSOR;      R         USER_TABLES%ROWTYPE;  BEGIN      P_CURSOR_TLHR(VARCURSOR); --這樣這個游標就有值了      LOOP          FETCH VARCURSOR              INTO R;          EXIT WHEN VARCURSOR%NOTFOUND;          DBMS_OUTPUT.PUT_LINE(R.TABLE_NAME);      END LOOP;  END;

當然,P_CURSOR_TLHR也可以返回多個系統游標,需要設置多個OUT類型的參數。

下面的例子返回自定義游標:

--定義全局變量  CREATE OR REPLACE PACKAGE pkg_package AS      TYPE type_cursor IS REF CURSOR;      TYPE type_record IS RECORD(          table_name VARCHAR2(32),          TABLESPACE_NAME VARCHAR2(32)   );  END;  --創建返回遊標的存儲過程  CREATE OR REPLACE PROCEDURE P_TEMP_PROCEDURE(CUR_OUT_ARG OUT PKG_PACKAGE.TYPE_CURSOR) IS  BEGIN    OPEN CUR_OUT_ARG FOR      SELECT D.TABLE_NAME, D.TABLESPACE_NAME        FROM USER_TABLES D       WHERE ROWNUM <= 5;  END;  --調用  DECLARE      cur_out_arg pkg_package.type_cursor;      rec_arg     pkg_package.type_record;  BEGIN      p_temp_procedure(cur_out_arg);      FETCH cur_out_arg          INTO rec_arg;      dbms_output.put_line(rec_arg.table_name);      dbms_output.put_line(rec_arg.TABLESPACE_NAME);  END;

下面給出一個函數返回系統游標的例子:

CREATE OR REPLACE FUNCTION F_GET_SYS_REFCURSOR_LHR(P_EMPNO NUMBER) RETURN SYS_REFCURSOR IS      CUR_SYS SYS_REFCURSOR;    BEGIN      OPEN CUR_SYS FOR        SELECT LEVEL P_LEVEL,               T.EMPNO,               T.ENAME,               T.MGR,               (LPAD(' ', 6 * (LEVEL - 1)) || LEVEL || ':' || T.ENAME || '(' ||               T.EMPNO || ')') NAME_ALL,               SUBSTR(SYS_CONNECT_BY_PATH(T.ENAME, '=>'), 3) ALL_NAME_LEVEL,               CONNECT_BY_ROOT(T.ENAME) ROOT,               DECODE(CONNECT_BY_ISLEAF, 1, 'Y', 0, 'N') IS_LEAF          FROM SCOTT.EMP T         START WITH T.EMPNO=P_EMPNO        CONNECT BY NOCYCLE MGR = PRIOR EMPNO;      RETURN CUR_SYS;    EXCEPTION      WHEN OTHERS THEN        NULL;    END;

查詢:

SELECT F_GET_SYS_REFCURSOR_LHR(7566)  FROM   DUAL;

結果如下所示:

有關存儲過程或函數返回集合類型的寫法有多種,作者把這多種方式寫成了一個包,已發佈到博客(地址:http://blog.itpub.net/26736162/viewspace-2131977/)和公眾號上,讀者可自行下載閱讀。

本文選自《Oracle程序員面試筆試寶典》,作者:李華榮。

About Me:小麥苗

● 本文作者:小麥苗,只專註於數據庫的技術,更注重技術的運用

● 作者博客地址:http://blog.itpub.net/26736162/abstract/1/

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

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

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