Question

I have two functions which both return a TYPE, i.e.:

CREATE OR REPLACE FUNCTION myUser.f_myFunction
(
    myId IN RAW := NULL
) RETURN myUser.myType
AS
    ResultTable myUser.myType;
BEGIN
    ...
    -- fill ResultTable
    ...

    RETURN ResultTable;
END;

Now I want to join them in a SELECT statement:

SELECT *
FROM myUser.f_myFunction1() f1
JOIN myUser.f_myFunction2() f2 ON f1.xy = f2.yz;

But if I include a function in a SELECT statement, I get an error:

SELECT * FROM myUser.f_myFunction();
                                 *
ERROR in Line 1:
ORA-00933: SQL command not properly ended
    ORA-04044: procedure, function, package, or type is not allowed here

.

EDIT: Nesting the function call in a TABLE() clause gives the following error:

SELECT * FROM TABLE(myUser.f_myFunction())
              *
ERROR at line 1:
ORA-22905: cannot access rows from a non-nested table item

Then I tried to cast it, but:

SELECT * FROM TABLE(CAST(myUser.f_myFunction() AS myUser.myType))
                             *
ERROR at line 1:
ORA-22907: invalid CAST to a type that is not a nested table or VARRAY

And:

SELECT * FROM TABLE(CAST(myUser.f_myFunction() AS myUser.myTypeTable))
                             *
ERROR at line 1:
ORA-00932: inconsistent datatypes: expected - got myUser.myType

.

EDIT 2: Here are the definitions of the types (sorry, should have included them earlier):

CREATE TYPE myUser.myType AS OBJECT (
    ....
);

CREATE TYPE myUser.myTypeTable IS TABLE OF myUser.myType;

.

EDIT 3: Got it working like this:

CREATE OR REPLACE FUNCTION myUser.f_myFunction
(
    myId IN RAW := NULL
) RETURN myUser.myTypeTable
AS
    ResultTable myUser.myTypeTable;
BEGIN

    SELECT myUser.myType(x.Column1, x.Column2, ...)
        BULK COLLECT INTO ResultTable
        FROM myUser.myTable x
        WHERE ...

    RETURN ResultTable;
END;
Was it helpful?

Solution

There are at least three ways to do this: 1) CCreate a nested table 2) Call the function multiple times 3) Create a function to access each attribute.

Sample objects

create or replace type myType as object
(
    wx number,
    xy number
);


CREATE OR REPLACE FUNCTION f_myFunction
(
    myId IN RAW := NULL
) RETURN myType
AS
    ResultTable myType := myType(1, 2);
BEGIN
    RETURN ResultTable;
END;
/

Method 1: Create a nested table

As Luke Woodward pointed out, the features TABLE and CAST require a nested table of types. Even if you only intend to use a single element.

create or replace type myType_nt is table of myType;

CREATE OR REPLACE FUNCTION f_myFunction_nt
(
    myId IN RAW := NULL
) RETURN myType_nt
AS
    ResultTable myType_nt := myType_nt(myType(1, 2));
BEGIN
    RETURN ResultTable;
END;
/

select wx, xy from table(f_myFunction_nt());

WX  XY
--  --
1   2

Method 2: Repeat the function call for each attribute

Extra objects are unnecessary if the function is called once for each attribute. This is repetitive and potentially costly. Setting the function to deterministic may prevent the function from actually being called multiple times.

select wx, xy from
(
    select f_myFunction().wx wx, f_myFunction().xy xy from dual
);

WX  XY
--  --
1   2

It seems like there should be an easier way to do this. Oracle does support calling the function once and returning the type with both attributes. But there is no way to meaningfully use that type. The attributes are not easily accessible from SQL.

SQL> select * from (select f_myFunction() from dual);

F_MYFUNCTION()(WX, XY)
-----------------------------------------------------
MYTYPE(1, 2)

The intuitive way, simply using each attribute name, does not work.

SQL> select wx, xy from (select f_myFunction() from dual);
select wx, xy from (select f_myFunction() from dual)
           *
ERROR at line 1:
ORA-00904: "XY": invalid identifier

Method 3: Create a function for each attribute

You can access the attributes with a function. This requires adding new objects but it keeps your main function the same.

create or replace function get_wx(p_myType myType) return number is
begin
    return p_myType.wx;
end;
/

create or replace function get_xy(p_myType myType) return number is
begin
    return p_myType.xy;
end;
/

select get_wx(asdf) wx, get_xy(asdf) xy
from (select f_myFunction() asdf from dual);

WX  XY
--  --
1   2
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top