【问题标题】:Create an Oracle function that returns a table创建一个返回表的 Oracle 函数
【发布时间】:2011-02-19 06:26:36
【问题描述】:

我正在尝试在包中创建一个返回表的函数。我希望在包中调用该函数一次,但能够多次重复使用其数据。虽然我知道我在 Oracle 中创建临时表,但我希望保持干燥。

到目前为止,这就是我所拥有的:

标题:

CREATE OR REPLACE PACKAGE TEST AS 

    TYPE MEASURE_RECORD IS RECORD (
      L4_ID VARCHAR2(50),
      L6_ID VARCHAR2(50),
      L8_ID VARCHAR2(50),
      YEAR NUMBER,
      PERIOD NUMBER,
      VALUE NUMBER
    );

    TYPE MEASURE_TABLE IS TABLE OF MEASURE_RECORD;

    FUNCTION GET_UPS(
      TIMESPAN_IN IN VARCHAR2 DEFAULT 'MONTLHY',
      STARTING_DATE_IN DATE,
      ENDING_DATE_IN DATE  
    ) RETURN MEASURE_TABLE;

END TEST;

主体:

CREATE OR REPLACE PACKAGE BODY TEST AS 

  FUNCTION GET_UPS (
    TIMESPAN_IN IN VARCHAR2 DEFAULT 'MONTLHY',
    STARTING_DATE_IN DATE,
    ENDING_DATE_IN DATE
  ) RETURN MEASURE_TABLE IS

    T MEASURE_TABLE;

  BEGIN

        SELECT  ...
        INTO    T
        FROM    ...

      ;

  RETURN T;

  END GET_UPS;

END TEST;

标题编译,正文没有。一条错误消息是“没有足够的值”,这可能意味着我应该选择 MEASURE_RECORD,而不是 MEASURE_TABLE。

我错过了什么?

【问题讨论】:

    标签: plsql oracle10g package


    【解决方案1】:
      CREATE OR REPLACE PACKAGE BODY TEST AS 
    
       FUNCTION GET_UPS(
       TIMESPAN_IN IN VARCHAR2 DEFAULT 'MONTLHY',
       STARTING_DATE_IN DATE,
       ENDING_DATE_IN DATE
       )RETURN MEASURE_TABLE IS
    
        T MEASURE_TABLE;
    
     BEGIN
    
        **SELECT   MEASURE_RECORD(L4_ID , L6_ID ,L8_ID ,YEAR ,
                 PERIOD,VALUE )  BULK COLLECT  INTO    T
        FROM    ...**
    
      ;
    
       RETURN T;
    
       END GET_UPS;
    
    END TEST;
    

    【讨论】:

    • 调用这个函数的语法是什么? select * from TABLE(TEST.GET_UPS(...))?
    【解决方案2】:

    要一次返回整个表,您可以将 SELECT 更改为:

    SELECT  ...
    BULK COLLECT INTO T
    FROM    ...
    

    这仅适用于不太大的结果,因为它们都必须在返回之前在内存中累积;否则请考虑 Charles 建议的流水线函数,或返回 REF CURSOR。

    【讨论】:

      【解决方案3】:

      我想你想要一个pipelined table function

      类似这样的:

      CREATE OR REPLACE PACKAGE test AS
      
          TYPE measure_record IS RECORD(
             l4_id VARCHAR2(50), 
             l6_id VARCHAR2(50), 
             l8_id VARCHAR2(50), 
             year NUMBER, 
             period NUMBER,
             VALUE NUMBER);
      
          TYPE measure_table IS TABLE OF measure_record;
      
          FUNCTION get_ups(foo NUMBER)
              RETURN measure_table
              PIPELINED;
      END;
      
      CREATE OR REPLACE PACKAGE BODY test AS
      
          FUNCTION get_ups(foo number)
              RETURN measure_table
              PIPELINED IS
      
              rec            measure_record;
      
          BEGIN
              SELECT 'foo', 'bar', 'baz', 2010, 5, 13
                INTO rec
                FROM DUAL;
      
              -- you would usually have a cursor and a loop here   
              PIPE ROW (rec);
      
              RETURN;
          END get_ups;
      END;
      

      为简单起见,我去掉了你的参数,并没有在函数中实现循环,但是你可以看到原理。

      用法:

      SELECT *
        FROM table(test.get_ups(0));
      
      
      
      L4_ID L6_ID L8_ID       YEAR     PERIOD      VALUE
      ----- ----- ----- ---------- ---------- ----------
      foo   bar   baz         2010          5         13
      1 row selected.
      

      【讨论】:

      • 我假设使用游标会比使用临时表更占用资源(即更慢)。我对么?我需要对这个函数的结果进行分组。这会改变您的建议吗?
      • 关于光标,是的,我相信它们对性能不是很好,所以像 Tony 提到的那样做 BULK COLLECT 可能会更快,然后遍历数组。这取决于您正在处理的行数和其他性能考虑因素。至于对结果进行分组,我不确定,但在任何情况下,临时表似乎都比流水线函数更有效。我建议进行更多的研究(或者更好的是,进行实验)。我会在asktom.oracle.com 提问。
      • " SELECT * FROM table(test.get_ups(0)); " 非常感谢!这真的很有帮助,我有一个返回表的函数,当我只执行“Select my_package.function from dual;”时它返回了一些我根本不知道如何使用的对象
      • @IgbyLargeman 不错的例子,有返回多行的例子吗?
      猜你喜欢
      • 2016-08-18
      • 2022-01-25
      • 2016-10-12
      • 1970-01-01
      • 1970-01-01
      • 2020-08-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多