【问题标题】:How can return a nested table a function?如何将嵌套表返回一个函数?
【发布时间】:2020-04-25 10:46:06
【问题描述】:

我使用 Bulk Collect 将数据带到 V_EMP 嵌套表中。 Begin-End之间怎么调用函数?

DECLARE

    TYPE T_REC IS RECORD
    (
    T_TITLE VARCHAR2,
    T_YEAR NUMBER(2,1)
    );

    TYPE T_EMP IS TABLE OF T_REC%TYPE;
    V_EMP T_EMP;
    Z_EMP T_EMP;
    V_EMP_ID NUMBER := 101;

    FUNCTION HIST(V_EMP_ID EMPLOYEES.EMPLOYEE_ID%TYPE)
        RETURN V_EMP;

    BEGIN

        SELECT JOB_TITLE T_TITLE, ROUND((END_DATE - START_DATE) / 365,1) T_YEAR
        BULK COLLECT INTO V_EMP
        FROM JOB_HISTORY INNER JOIN JOBS USING(JOB_ID)
        WHERE EMPLOYEE_ID = V_EMP_ID
        ORDER BY START_DATE;

        RETURN V_EMP;
    END HIST;

BEGIN

    Z_EMP := HIST(V_EMP_ID);

    FOR C IN (SELECT T_TITLE, T_YEAR FROM Z_EMP)
    LOOP
        DBMS_OUTPUT.PUT_LINE(C.T_TITLE, C.T_YEAR);
    END LOOP;

END;

【问题讨论】:

    标签: oracle plsql


    【解决方案1】:

    首先,您的类型声明不完全正确。

    在您的类型T_REC 中,您需要声明VARCHAR2 列的大小。我在这里以100 为例:

        TYPE T_REC IS RECORD
        (
        T_TITLE VARCHAR2(100),
        T_YEAR NUMBER(2,1)
        );
    

    其次是行

        TYPE T_EMP IS TABLE OF T_REC%TYPE;
    

    不正确:T_REC 本身就是一种类型,因此您无需为其指定 %TYPE 属性。请尝试以下方法。

        TYPE T_EMP IS TABLE OF T_REC;
    

    您定义函数的方式也存在一些问题:

        FUNCTION HIST(V_EMP_ID EMPLOYEES.EMPLOYEE_ID%TYPE)
            RETURN V_EMP;
    
        BEGIN
            -- ...
    

    RETURN 子句需要使用类型,但V_EMP 是局部变量。此外,您需要包含关键字IS 来告诉PL/SQL 编译器以下块构成函数的主体,而不是用分号结束声明。将这些更改放在一起,我们有:

        FUNCTION HIST(V_EMP_ID EMPLOYEES.EMPLOYEE_ID%TYPE)
            RETURN T_EMP
        IS
        BEGIN
            -- ...
    

    用声明解决这些问题后,我们可以查看底部的BEGIN 块。第一个问题是您不能编写SELECT T_TITLE, T_YEAR FROM Z_EMP 来查询包含嵌套表的变量。相反,您必须将其包装在对TABLE 的调用中,即SELECT T_TITLE, T_YEAR FROM TABLE(Z_EMP)

    但是,这样做是行不通的。如果您尝试,您将收到错误PLS-00642: local collection types not allowed in SQL statements。这是因为您不能对仅在 PL/SQL 块中声明的类型运行 SQL 查询。您可以改为使用以下方法循环返回集合中的值:

        IF Z_EMP.COUNT = 0 THEN
            DBMS_OUTPUT.PUT_LINE('There are no records');
        ELSE
            FOR i IN Z_EMP.FIRST .. Z_EMP.LAST
            LOOP
                DBMS_OUTPUT.PUT_LINE(Z_EMP(i).T_TITLE || ', ' || Z_EMP(i).T_YEAR);
            END LOOP;
        END IF;
    

    请注意,在这种情况下,我们需要检查没有记录的集合:如果集合为空,Z_EMP.FIRSTZ_EMP.LAST 将是 NULL,您将得到一个 PL/SQL: numeric or value error 尝试使用它们FOR 循环中的范围。另请注意,DBMS_OUTPUT.PUT_LINE 仅接受一个参数:为避免此处出现错误,我将这两个值连接在一起并在它们之间使用逗号。

    或者,如果您真的想使用 SQL 查询来读取函数返回的值,则需要做更多的工作。您必须在 PL/SQL 块之外声明类型 T_RECT_EMP,如下所示:

    CREATE TYPE T_REC IS OBJECT
    (
        T_TITLE VARCHAR2(100 CHAR),
        T_YEAR NUMBER(2,1)
    );
    /
    
    CREATE TYPE T_EMP IS TABLE OF T_REC;
    /
    

    然后,您将在块中删除这些类型的声明。您还必须调整函数内的查询:而不是选择

            SELECT JOB_TITLE T_TITLE, ROUND((END_DATE - START_DATE) / 365,1) T_YEAR
    

    并且将这些字段映射到记录中,您必须从每个选定的行显式创建一个 T_REC 对象:

            SELECT T_REC(JOB_TITLE, ROUND((END_DATE - START_DATE) / 365,1))
    

    完成此操作后,底部的循环可以更改为以下内容:

            FOR C IN (SELECT T_TITLE, T_YEAR FROM TABLE(Z_EMP))
            LOOP
                DBMS_OUTPUT.PUT_LINE(C.T_TITLE || ', ' || C.T_YEAR);
            END LOOP;
    

    如果您愿意,也可以去掉检查Z_EMP.COUNT = 0:如果Z_EMP 为空,上述循环不会报告错误,尽管它不会生成任何输出。

    【讨论】:

      猜你喜欢
      • 2018-07-16
      • 2011-01-19
      • 1970-01-01
      • 2013-04-10
      • 1970-01-01
      • 1970-01-01
      • 2011-07-20
      • 1970-01-01
      • 2020-08-06
      相关资源
      最近更新 更多