【问题标题】:How to retrieve multiple rows from a stored function with oracle如何使用oracle从存储函数中检索多行
【发布时间】:2012-11-30 11:03:08
【问题描述】:

我正在尝试在 oracle 中创建一个返回多行的存储函数。

我的问题与this one 非常相似,只是我想获取select * 查询

简而言之,我想创建一个返回此查询结果的函数

select * from t_email_queue

我试过的是这样的:

create or replace
PACKAGE email_queue AS 

  type t_email_queue_type is table of T_EMAIL_QUEUE%ROWTYPE;  

  FUNCTION lock_and_get return t_email_queue_type;

END email_queue;

create or replace
PACKAGE BODY email_queue AS 

    FUNCTION lock_and_get RETURN t_email_queue_type AS 
      queue_obj t_email_queue_type;

      cursor c (lockid in varchar2) is select * from t_email_queue where lockedby = lockid;
      lockid varchar2(100) := 'alf';
    BEGIN

      OPEN c(lockid);
      FETCH c bulk collect INTO queue_obj;

      return queue_obj;

    END lock_and_get;

END email_queue;

包编译得很好,但是当我尝试用这个查询调用它时

select * from table(email_queue.lock_and_get);

Oracle 抛出以下错误

ORA-00902: invalid datatype
00902. 00000 -  "invalid datatype"
*Cause:    
*Action:
Error at Line: 1 Column: 20

我认为 Oracle 希望我在架构级别创建返回类型,但是当我尝试这样做时

create type t_email_queue_type is table of T_EMAIL_QUEUE%ROWTYPE;  

甲骨文抱怨

Type IMAIL.T_EMAIL_QUEUE_TYPE@imail dev
Error(1): PL/SQL: Compilation unit analysis terminated
Error(2,37): PLS-00329: schema-level type has illegal reference to IMAIL.T_EMAIL_QUEUE

有人能指出我正确的方向吗?我在这里错过了什么?

感谢阅读!

【问题讨论】:

    标签: oracle plsql


    【解决方案1】:

    对于不能执行 %ROWTYPE 的 SQL 类型,您必须键入每一列以匹配表*。

    *sys.anydataset 放在一边。但沿着这条路线走,编码要复杂得多。

    例如如果你的桌子是

    create table foo (id number, cola varchar2(1));
    

    然后

    create type email_queue_type is object (id number, cola varchar2(1));
    /
    create type t_email_queue_type as table of email_queue_type;
    /
    

    并使用该表 email_queue_type_tab 作为函数的输出。

    但我建议使用流水线函数,因为您当前的代码不可扩展。

    例如:

    SQL> create table foo (id number, cola varchar2(1));
    
    Table created.
    
    SQL>
    SQL> create type email_queue_type is object (id number, cola varchar2(1));
      2  /
    
    Type created.
    
    SQL> create type t_email_queue_type as table of email_queue_type;
      2  /
    
    Type created.
    
    SQL> insert into foo select rownum, 'a' from dual connect by level <= 10;
    
    10 rows created.
    
    SQL>
    SQL> create or replace PACKAGE email_queue AS
      2
      3
      4    FUNCTION lock_and_get return t_email_queue_type pipelined;
      5
      6  END email_queue;
      7  /
    
    Package created.
    
    SQL> create or replace PACKAGE BODY email_queue AS
      2
      3      FUNCTION lock_and_get RETURN t_email_queue_type pipelined AS
      4        queue_obj t_email_queue_type;
      5
      6      BEGIN
      7
      8       for r_row in (select * from foo)
      9              loop
     10                pipe row(email_queue_type(r_row.id, r_row.cola));
     11              end loop;
     12
     13      END lock_and_get;
     14
     15  END email_queue;
     16  /
    
    Package body created.
    
    SQL> select * from table(email_queue.lock_and_get());
    
            ID C
    ---------- -
             1 a
             2 a
             3 a
             4 a
             5 a
             6 a
             7 a
             8 a
             9 a
            10 a
    
    10 rows selected.
    
    SQL>
    

    【讨论】:

    • 这个解决方案的问题是,当我必须向表t_email_queue 添加一列时,我也必须修改我的类型email_queue_type,这很痛苦;在修改类型和包时,我必须停止调用包的 PHP 脚本,这不是一个选项。
    • @Alfwed 你在 11g 上吗?如果是这样,“版本”可能意味着您可以更新类型 + 包/类型,而无需为此停止 PHP 应用程序。
    • 是的,我正在运行 11g。这个函数我们大约每秒调用一次。你是说我可以在两次调用之间编辑表格、类型和包?
    • @Alfwed 好吧,您之前遇到过表格问题,但就像我说的版本一样,您在新版本中编辑类型 + 包(因此运行代码看不到它)。然后完成后将其设置为活动状态,因此任何新调用都会看到它。
    • 例如调用 A 进入,然后您编辑类型(在新版本中)然后调用 B 进入。调用 B 仍会看到旧类型。我建议你阅读它。例如看这里youtube.com/watch?v=cZSIaZCWaOs&t=7m6s
    【解决方案2】:

    如果您不是特别热衷于使用 SQL 类型,则可以使用 sys_refcursor 代替:

    create or replace package email_queue as 
        function lock_and_get return sys_refcursor;
    end email_queue;
    /
    
    create or replace package body email_queue as 
        function lock_and_get return sys_refcursor AS 
            c sys_refcursor;
            lockid varchar2(100) := 'alf';
        begin
            open c for
                select * from t_email_queue
                where lockedby = lockid;
    
             return c;
        end lock_and_get;
    end email_queue;
    /
    

    从 SQL*Plus 你可以这样称呼它:

    var cur refcursor;
    exec :cur := email_queue.lock_and_get;
    print cur
    

    并且exec 是一个简单匿名块的简写,如果您也可以从其他 PL/SQL 对象调用该块。但是,您不能做的是:

    select * from table(email_queue.lock_and_get);
    

    我不熟悉从 PHP 调用函数,但是从 Java 中,您可以直接将其用作可调用语句的返回值,因此您根本不需要 select * from table() 构造。我不知道您是否可以在 PHP 调用中执行匿名块,例如 begin $cur = email_queue.lock_and_get; end;,并将 $cur 作为您的结果集,然后您可以迭代?

    我意识到这不是一个完整的答案,因为 PHP 方面太模糊了,但可能会给你一些想法。

    【讨论】:

    • 好答案。我目前正在使用这种技术,但我不太喜欢它,因为从 PHP 调用这种函数有点奇怪。因此,我对“表返回类型”提出了疑问。
    • @Alfwed - 可能是没有意义的区别,但在过程中将 ref 光标作为 out 参数返回也是一种选择。在 PHP 中可能同样奇怪,但我不知道 *8-)
    【解决方案3】:

    如果您使用 PHP 并且想要访问 oracle 存储的函数。你可以使用这样的东西

    //Your connection details
    $conn = oci_connect($username, $password, '(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = 127.0.0.1)(PORT = 1521))(CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = XE)))' );
    
    /* Your query string; you can use oci_bind_by_name to bind parameters or just pass the variable in it*/
    
    $query = "begin :cur := functionName('".$param1."','".$param2."','".$param3."'); end;";
    $stid = oci_parse($conn, $query); 
    $OUTPUT_CUR = oci_new_cursor($conn);
    oci_bind_by_name($stid, ':cur', $OUTPUT_CUR, -1, OCI_B_CURSOR);
    oci_execute($stid); 
    oci_execute($OUTPUT_CUR);
    oci_fetch_all($OUTPUT_CUR, $res);
    
    // To get your result  
    var_dump($res);
    
    

    我希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-09-29
      • 1970-01-01
      • 1970-01-01
      • 2016-07-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-28
      相关资源
      最近更新 更多