使用%ROWTYPE和TABLE OF在包中创建表返回函数

时间:2014-08-13 20:42:22

标签: oracle oracle11gr2

我有一些我无法改变的表格。对于每个表,我需要在FUNCTION内创建一个PACKAGE,它返回一个与原始表具有相同数据/布局的临时表。我不想手动复制所有表列定义,而是使用%ROWTYPE等语句。

我想告诉Oracle:“此函数返回一个与原始表格XY布局相同的表格。”

请看一下这个例子。这是(遗留)表:

CREATE TABLE TEST.Emp (
    ID RAW(16),
    NAME VARCHAR2(10)
);
/

这些是包和类型定义:

CREATE OR REPLACE TYPE TEST.row_Emp AS OBJECT (
    ID RAW(16),
    NAME VARCHAR2(10)
);
/

CREATE OR REPLACE PACKAGE TEST.Emp_PKG AS
    TYPE t_Emp IS TABLE OF TEST.row_Emp INDEX BY BINARY_INTEGER;

    FUNCTION F_Emp_Select (
        VersionId INT DEFAULT NULL
    ) RETURN t_Emp;

END;
/

这是包体:

CREATE OR REPLACE PACKAGE BODY TEST.Emp_PKG AS

    FUNCTION F_Emp_Select (
        VersionId INT DEFAULT NULL
    ) RETURN t_Emp
    AS
        VersionVar INT := VersionId;
        v_ret t_Emp;
    BEGIN

        SELECT
            CAST(
                MULTISET(
                    SELECT ID, NAME FROM TEST.Emp
                ) AS t_Emp)                         -- <== this is line 15
            INTO v_ret
            FROM dual;

        RETURN v_ret;
    END;
END;
/

如果我在SQLPlus中执行此操作,则会收到以下错误:

Errors for PACKAGE BODY TEST.EMP_PKG:

LINE/COL ERROR
-------- -----------------------------------------------------------------
11/9     PL/SQL: SQL Statement ignored
15/22    PL/SQL: ORA-00902: invalid datatype

我做错了什么?

编辑:我需要这个用于更复杂的案例。此功能将用于其他功能和程序。例如,我需要能够对函数的结果进行连接:

SELECT ... FROM ...
INNER JOIN TEST.Emp_PKG.F_Emp_Select(...) ON ...

所以我不需要整个结果。

很抱歉这个混乱,我来自SQL Server,我多次做过这样的事情。

2 个答案:

答案 0 :(得分:3)

如果您需要将结果视为表格,则可以使用管道功能:<​​/ p>

CREATE OR REPLACE TYPE row_Emp AS OBJECT (
    ID RAW(16),
    NAME VARCHAR2(10)
);
/

CREATE OR REPLACE TYPE tab_Emp AS TABLE OF row_Emp
/

CREATE OR REPLACE PACKAGE Emp_PKG AS

    FUNCTION F_Emp_Select (
        VersionId INT DEFAULT NULL
    ) RETURN tab_Emp PIPELINED;

END;
/

CREATE OR REPLACE PACKAGE BODY Emp_PKG AS

    FUNCTION F_Emp_Select (
        VersionId INT DEFAULT NULL
    ) RETURN tab_EMP PIPELINED
    AS
    BEGIN

        FOR row IN (SELECT ID, NAME FROM Emp) LOOP
          PIPE ROW (row_Emp(row.ID, row.NAME));
        END LOOP;

        RETURN;
    END;
END;
/

请注意,必须在架构级别声明对象类型和表类型;表类型不能是PL / SQL-seclared表(集合)类型,因为它不能在纯SQL中使用,即使在其他PL / SQL中也是如此。遗憾的是%ROWTYPE是一个PL / SQL结构,因此您无法使用它来定义模式级表类型。

然后你可以这样做:

SELECT * FROM TABLE(Emp_PKG.F_Emp_Select(<optional versionId>));

...或在联接中使用它:

SELECT ... FROM ...
INNER JOIN TABLE(TEST.Emp_PKG.F_Emp_Select(...)) ON ...

SQL FIddle demo

答案 1 :(得分:2)

假设你的目标是返回一个集合,而不是一个临时表,它比你的例子复杂一点

CREATE OR REPLACE PACKAGE emp_pkg
AS
  TYPE emp_typ IS TABLE OF emp%rowtype index by binary_integer;

  FUNCTION get_emps
    RETURN emp_typ;
END;

CREATE OR REPLACE PACKAGE BODY emp_pkg
AS
  FUNCTION get_emps
    RETURN emp_typ
  IS
    l_emps emp_typ;
  BEGIN
    SELECT *
      BULK COLLECT INTO l_emps
      FROM emp;

    RETURN l_emps;
  END;
END;

现在,从架构上讲,我会非常非常关注一个解决方案,该解决方案涉及从表中选择所有数据到PL / SQL集合中。 PL / SQL集合必须完全存储在会话的SGA中,这是相对昂贵的服务器RAM。如果您的表中有数千或数万行,那么服务器上的空间可能相当大,特别是如果可能有许多不同的会话都在大致同一时间调用这些过程。如果你的表都有几百行,一次只有一个会话将使用这些函数,也许这种方法就足够了。