Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to retrieve multiple rows from a stored function with oracle

Tags:

oracle

plsql

I'm trying to create a stored function in oracle that returns multiple rows.

My question is very similar to this one except that I want to fetch a select * query

In a nutshell, I want to create a function which returns the result of this query

select * from t_email_queue

What I've tried is this :

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;

The package compiles just fine but when I try to call it with this query

select * from table(email_queue.lock_and_get);

Oracle throws the following error

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

I think Oracle want me to create my return type at the schema level but when I try to do

create type t_email_queue_type is table of T_EMAIL_QUEUE%ROWTYPE;  

Oracle complains

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

Can someone point me to the right direction? What am I missing here?

Thanks for reading!

like image 762
Alfwed Avatar asked Dec 12 '12 17:12

Alfwed


People also ask

Can function return multiple rows in Oracle?

Ideally you should not try to get the multiple row result as how you are trying to. You cannot get the desired result as per the way you are trying to get it. SELECT ven_mod || ': '|| names into l_return from tobject where object_id in (p1_in,p2_in,p3_in,p4_in); RETURN LTRIM(l_return);

Can we return multiple rows from function?

In C or C++, we cannot return multiple values from a function directly. In this section, we will see how to use some trick to return more than one value from a function.

How can I return multiple values from a function in Oracle?

SQL> create or replace 2 type three_values as object 3 ( val_1 int, 4 val_2 date, 5 val_3 varchar2(10) 6 ); 7 / Type created. SQL> SQL> create or replace 2 function f return three_values is 3 begin 4 return 5 three_values(1,sysdate,'hello'); 6 end; 7 / Function created.

Which function is used to fetch multiple records?

The multi-row function in SQL is used to retrieve data per set of rows at the time when we work on the group by clause we use the Multi-Row Function.


3 Answers

If you aren't particularly keen on having the SQL type, you could do this with a sys_refcursor instead:

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;
/

From SQL*Plus you could call it something like:

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

and as exec is shorthand for a simple anonymous block you can call if from other PL/SQL objects too. What you can't do, though, is:

select * from table(email_queue.lock_and_get);

I'm not familiar with calling functions from PHP, but from Java you can use this directly as the return from a callable statement, so you don't need the select * from table() construct at all. I have no idea if you can execute an anonymous block in a PHP call, something like begin $cur = email_queue.lock_and_get; end;, and have $cur as your result set that you can then iterate through?

I realise this isn't a complete answer as the PHP side is way too vague, but might give you some ideas.

like image 23
Alex Poole Avatar answered Nov 09 '22 16:11

Alex Poole


with SQL types you cant do %ROWTYPE, you'd have to type each column to match the table*.

*sys.anydataset aside. but going down that route is a lot more complex coding.

e.g. if your table was

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

then

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

and use that table email_queue_type_tab as the output from your function.

but i'd recommend a pipelined function, as your current code isn't scalable.

eg:

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>
like image 170
DazzaL Avatar answered Nov 09 '22 18:11

DazzaL


If you are using PHP and you want to access a oracle stored function. You can make use of something like this

//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);

I hope this helps.

like image 37
Deolu Philip Avatar answered Nov 09 '22 16:11

Deolu Philip