Tail call for pipelined functions - oracle

I have some pipelined function:
create type my_tab_type as table of ...
create function my_func (X in number) return my_tab_type pipelined as
begin
loop
...
pipe row (...);
end loop;
return;
end;
Now I want to create another pipelined function my_func_zero which does the same as my_func but for fixed value of parameter: my_func_zero must be equivalent to my_func(0).
Can I implement my_func_zero without senseless and boring loop for processing every row returned by select * from table(my_func(0))?
P.S. That thread is a bit similar, but it does not contain answer to my question.

It's possible, but only if you don't declare your second function as pipelined because all functions of this type return results on row-by-row basis.
If you omit this requirement you can reach your target with bulk collect if you need typed cursor:
create function my_zero_func return my_tab_type
as
res_table my_tab_type;
begin
select my_type(field1, field2)
bulk collect into res_table
from table(my_func(0));
return res_table;
end;
Alternatively you can use untyped cursor:
create function my_ref_zero_func return sys_refcursor
as
vRes sys_refcursor;
begin
open vRes for select * from table(my_func(0));
return vRes;
end;
SQLFiddle
In a client application my_ref_zero_func results may be used without changes, but in the SQLFiddle it converted to XML representation because there are no way to demonstrate ref cursor with this tool.

Related

Oracle pipelined function with generic record type?

I have a pipelined function which returns results of a SQL query:
function my_pipelined_function(...)
return rec_t
PIPELINED is
begin
for l in (select * from ... join ... where ...) loop
PIPE ROW(l);
end loop;
return;
end;
This works, but I have to define the record and it's type in the package header:
TYPE rec IS RECORD(
some_date_param date,
some_number_param number,
...
);
TYPE rec_t IS TABLE OF rec;
Ugly. Isn't there a way to avoid having to declare the "hard-coded" record with all types defined, one by one?
I saw the ANYDATA type, but I can't make it work. Should I use this approach, if yes, how to do it? If not, should I use some kind of ref cursor, or is this simply not possible at all in Oracle?
Thanks

PL/SQL Function - Bulk Collect into and Pipe Row

I am new to PL/SQL have issue with output value of a function.
I want to execute a SQL statement in a function and return the results. The function will be executed with following command: select * from table(mypkg.execute_query('1'));
I was using following article as refence "Bulk Collect Into" and "Execute Immediate" in Oracle, but without success.
It seems that I am using wrong data type. System returns issue on following line: PIPE Row(results)
create or replace package mypkg
as
type node is table of edges%ROWTYPE;
function execute_query (startNode in varchar2) RETURN node PIPELINED;
end;
create or replace package body mypkg
as
function execute_query(startNode in varchar2) RETURN node PIPELINED
AS
results node;
my_query VARCHAR2(100);
output VARCHAR2(1000);
c sys_refcursor;
BEGIN
my_query := 'SELECT DISTINCT * FROM EDGES WHERE src='|| startNode;
open c for my_query;
loop
fetch c bulk collect into results limit 100;
exit when c%notfound;
PIPE Row(results);
end loop;
close c;
END;
end;
I tried several options with cursor but wasn't able to return the value. If you have idea how to return the data by using something else than PIPELINED, please let me know.
Thanks for your support!
Fixed body:
create or replace package body mypkg
as
function execute_query(startNode in varchar2) RETURN node PIPELINED
AS
results node;
my_query VARCHAR2(100);
output VARCHAR2(1000);
c sys_refcursor;
BEGIN -- don't use concatenation, it leads to sql injections:
my_query := 'SELECT DISTINCT * FROM EDGES WHERE src=:startNode';
-- use bind variables and bind them using cluase "using":
open c for my_query using startNode;
loop
fetch c bulk collect into results limit 100;
-- "results" is a collection, so you need to iterate it to pipe rows:
for i in 1..results.count loop
PIPE Row(results(i));
end loop;
exit when c%notfound;
end loop;
close c;
END;
end;
/

Nested PIPELINED function in pl/sql

I have to write a nested pipelined function in pl/sql which I tried implementing in the following manner.
create package body XYZ AS
function main_xyz return data_type_1 pipelined is
begin
--code
pipe row(sub_func);
end;
function sub_func return data_type_1 pipelined is
begin
--code
pipe row(sub_func_1);
end;
function sub_func_1 return data_type_1 pipelined is
begin
--code
pipe row(main_abc);
end;
end;
create package body abc AS
function main_abc return data_type_2 pipelined is
var data_type_2;
begin
--code
return var;
end;
end;
However, I get the following error
[Error] PLS-00653 : PLS-00653: aggregate/table functions are not
allowed in PL/SQL scope
Where am I going wrong? Is it syntax or logic?
Pipelined functions provide rows one by one (on demand), so you cannot put all the rows at one time from pipelined function.
Seems to me you need to change main_xyz this way:
function main_xyz return data_type_1 pipelined is
begin
--code
FOR rec IN (select * from table(XYZ.sub_func)) LOOP
pipe row(rec);
END LOOP;
end;
Consider that sub_func must be in specification of XYZ package since everything you use in SQL queries including PIPELINED functions are to be public (i.e. visible to a user who runs query).
UPDATE: I forget to alert: do not abuse pipelined functions (if you have another choice) - queries using them might have lame performance because DB engine cannot build a good execution plan for "unpredictable piped rows".

Oracle PL/SQL array input into parameter of pipelined function

I am new to PL/SQL. I have created a pipelined function inside a package which takes as its parameter input an array of numbers (nested table).
But I am having trouble trying to run it via an sql query. Please see below
my input array
CREATE OR REPLACE TYPE num_array is TABLE of number;
my function declaration
CREATE OR REPLACE PACKAGE "my_pack" as
TYPE myRecord is RECORD(column_a NUMBER);
TYPE myTable IS TABLE of myRecord;
FUNCTION My_Function(inp_param num_array) return myTable PIPELINED;
end my_pack;
my function definition
CREATE OR REPLACE PACKAGE BODY "my_pack" as
FUNCTION My_Function(inp_param num_array) return myTable PIPELINED as
rec myRecord;
BEGIN
FOR i in 1..inp_param.count LOOP
FOR e IN
(
SELECT column_a FROM table_a where id=inp_param(i)
)
LOOP
rec.column_a := e.column_a;
PIPE ROW (rec);
END LOOP;
END LOOP;
RETURN;
END;
end my_pack;
Here is the latest code I've tried running from toad. But it doesn't work
declare
myarray num_array;
qrySQL varchar2(4000);
begin
myarray := num_array(6341,6468);
qrySQL := 'select * from TABLE(my_pack.My_Function(:myarray))';
execute immediate qrySQL;
end;
So my question is how can I feed an array into this pipelined function from either TOAD or SQL Developer. An example would be really handy.
Thanks
The error is fairly clear, you have a bind variable that you haven't assigned anything to. You need to pass your actual array with:
qrySQL := 'select * from TABLE(my_pack.My_Function(:myarray))';
execute immediate qrySQL using myarray;
It's maybe more useful, if you want to call it from PL/SQL, to use static SQL as a cursor:
set serveroutput on
declare
myarray num_array;
begin
myarray := num_array(6341,6468);
for r in (select * from TABLE(my_pack.My_Function(myarray))) loop
dbms_output.put_line(r.column_a);
end loop;
end;
/
Or just query it statically as a test, for fixed values:
select * from TABLE(my_pack.My_Function(num_array(6341,6468)));
SQL Fiddle with some minor tweaks to the function to remove errors I think came from editing to post.

What is the standard way to return records from an Oracle function?

I really avoided the "best" word for my question but it really is the most suitable word for it.
What's the best(most efficient) way of returning records from a function?
Currently I have something like:
FUNCTION myFunct(param1 VARCHAR2) RETURN SYS_REFCURSOR AS
myCursor SYS_REFCURSOR;
BEGIN
OPEN myCursor FOR
SELECT *
FROM myTable
WHERE field = param1;
RETURN(myCursor);
END myFunct;
I can run this fine but with everything else I am reading like (TABLE type, implicit cursor, etc) I am really confused about what is most suitable.
P.S. how can I loop over this cursor after I call it from a proc?
EDIT:
I've read that I can only iterate through cursors ONCE (forums.oracle.com/thread/888365) but in reality I want to loop contents several times. Does this mean that I am opt to use associative arrays instead?
create or replace
PACKAGE example_pkg AS
/*
** Record and nested table for "dual" table
** It is global, you can use it in other packages
*/
TYPE g_dual_ntt IS TABLE OF SYS.DUAL%ROWTYPE;
g_dual g_dual_ntt;
/*
** procedure is public. You may want to use it in different parts of your code
*/
FUNCTION myFunct(param1 VARCHAR2) RETURN SYS_REFCURSOR;
/*
** Example to work with a cursor
*/
PROCEDURE example_prc;
END example_pkg;
create or replace
PACKAGE BODY example_pkg AS
FUNCTION myFunct(param1 VARCHAR2) RETURN SYS_REFCURSOR
AS
myCursor SYS_REFCURSOR;
BEGIN
OPEN myCursor FOR
SELECT dummy
FROM dual
WHERE dummy = param1;
RETURN(myCursor);
END myFunct;
PROCEDURE example_prc
AS
myCursor SYS_REFCURSOR;
l_dual g_dual_ntt; /* With bulk collect there is no need to initialize the collection */
BEGIN
-- Open cursor
myCursor := myFunct('X');
-- Fetch from cursor / all at onece
FETCH myCursor BULK COLLECT INTO l_dual;
-- Close cursor
CLOSE myCursor;
DBMS_OUTPUT.PUT_LINE('Print: ');
FOR indx IN 1..l_dual.COUNT LOOP
DBMS_OUTPUT.PUT_LINE('element: ' || l_dual(indx).dummy );
END LOOP;
END example_prc;
END example_pkg;
EXECUTE example_pkg.example_prc();
/*
Print:
element: X
*/
Please take a look at this link: http://www.oracle-base.com/articles/misc/using-ref-cursors-to-return-recordsets.php
You might find it useful...

Resources