I have created one pipelined function and another non-pipelined function.
But both functions when called from select statement, all results are shown only after complete execution of loop.
Why the pipelined function is not returning value as soon as the data for the each row is ready?
SELECT * FROM TABLE(GET_TAB(10,1));--Normal function call
SELECT * FROM TABLE(GET_TAB_P(10,1));--Pipelined function call
create or replace TYPE T_TF_ROW AS OBJECT(CNT NUMBER, DESCRIPTION VARCHAR2(50));
create or replace TYPE T_TF_TAB AS TABLE OF T_TF_ROW;
create or replace FUNCTION GET_TAB(P_ROWS IN NUMBER, P_SLEEP IN NUMBER)
RETURN T_TF_TAB
AS
V_T_TF_TAB T_TF_TAB:=T_TF_TAB();
BEGIN
FOR I IN 1..P_ROWS LOOP
DBMS_LOCK.SLEEP(P_SLEEP);
V_T_TF_TAB.EXTEND;
V_T_TF_TAB(V_T_TF_TAB.LAST):=T_TF_ROW(I,'DESCRIPTION OF : '||I);
END LOOP;
RETURN V_T_TF_TAB;
END;
create or replace FUNCTION GET_TAB_P(P_ROWS IN NUMBER, P_SLEEP IN NUMBER)
RETURN T_TF_TAB PIPELINED
AS
BEGIN
FOR I IN 1..P_ROWS LOOP
DBMS_LOCK.SLEEP(P_SLEEP);
PIPE ROW(T_TF_ROW(I,'DESCRIPTION FOR ' || I));
END LOOP;
RETURN;
END;
Normal function result
Pipelined function result
Your pipelined function is working correctly, but your client's fetch size optimization is retrieving N rows at-a-time before it returns anything. Disable or lower the fetch size and you should start seeing individual rows returned. However, in production you do not want to disable that optimization.
In SQL*Plus, you would run this command to see one-row-at-a-time:
set arraysize 1;
(That actually returns two rows at a time for a reason I can't remember. There is a workaround, but unless you are writing some sort of progress-bar program you probably don't care about getting exactly one row-at-a-time.)
I need to pass a locally defined table type to a function which is a pipelined function returning another locally defined table type.
Here is sample data:
create table my_tab
(i NUMBER,
n VARCHAR2(30));
insert into my_tab values (1, 'Peter');
insert into my_tab values (2, 'Dakshesh');
insert into my_tab values (1, 'Maggie');
insert into my_tab values (3, 'Madhu');
commit;
My code is:
CREATE OR REPLACE PACKAGE my_pkg IS
TYPE t_col IS RECORD(
i NUMBER,
n VARCHAR2(30));
TYPE t_nested_table IS TABLE OF t_col;
TYPE t_number IS TABLE OF NUMBER;
FUNCTION iterate_table RETURN t_number PIPELINED;
FUNCTION return_table(in_t_num t_number) RETURN t_nested_table PIPELINED;
g_number t_number ;
g_nested_number t_nested_table ;
END my_pkg;
/
Body:
CREATE OR REPLACE PACKAGE BODY my_pkg IS
FUNCTION iterate_table RETURN t_number PIPELINED IS
BEGIN
IF ( (g_number IS NOT NULL) AND (g_number.EXISTS (1)))
THEN
FOR i IN 1 .. g_number.COUNT
LOOP
IF g_number (i) IS NOT NULL
THEN
PIPE ROW (g_number (i));
END IF;
END LOOP;
END IF;
RETURN;
EXCEPTION
WHEN OTHERS
THEN
RAISE;
END iterate_table;
FUNCTION return_table(in_t_num t_number) RETURN t_nested_table PIPELINED IS
l_row t_nested_table ;
CURSOR cur_test IS
select mt.i, mt.n
from my_tab mt, TABLE(iterate_table ) tab
where mt.i = tab.column_value;
BEGIN
OPEN cur_test;
FETCH cur_test BULK COLLECT into l_row;
CLOSE cur_test;
FOR i IN 1..l_row.COUNT
LOOP
PIPE ROW(l_row(i));
END LOOP;
RETURN;
END return_table;
END my_pkg;
/
Now this code compiles successfully, when I try to invoke it like a pipelined function, it gives error-
select * from table(my_pkg.return_table(my_pkg.t_number(1)));
Error-
ORA-00902: invalid datatype
00902. 00000 - "invalid datatype"
*Cause:
*Action:
Error at Line: 14 Column: 41
The two pre-requisites for this code are -
the collections should be all locally defined.
the function should be pipelined.
Help!!
I am not getting any error with table and its a single table so no foreign key constraints hold.
It is not possible in oracle 11. I don't know why oracle throws "invalid data type".
If you put this query into anonymous block you will receive PLS-00642: Local Collection Types Not Allowed in SQL Statement
If you don't want or you cannot create sql level collection. The solution is use predefined type. Good source of predefined collection is Oracle Data Cartridge. Replace all t_number with ODCINumberList.
predefined collections
In Oracle 10 and 11, it is not possible to use collections in the SQL scope that have been defined in a PL/SQL scope. In Oracle 12, what you are trying should work.
If you want to do this then declare your types in the SQL scope (not in the package).
CREATE OR REPLACE TYPE t_col IS OBJECT(
i NUMBER,
n VARCHAR2(30)
);
/
CREATE OR REPLACE TYPE t_nested_table IS TABLE OF t_col;
/
CREATE OR REPLACE TYPE t_number IS TABLE OF NUMBER;
/
CREATE OR REPLACE PACKAGE my_pkg IS
FUNCTION iterate_table RETURN t_number PIPELINED;
FUNCTION return_table(in_t_num t_number) RETURN t_nested_table PIPELINED;
g_number t_number ;
g_nested_number t_nested_table ;
END my_pkg;
/
Hey guys I am having an issue where I get an error when I try to add the second private header underneath my first private header.
CREATE OR REPLACE PACKAGE TEST IS
PROCEDURE TEST2(VARIABLE1 IN NUMBER, VARIABLE2 OUT NUMBER);
END;
CREATE OR REPLACE PACK BODY TEST IS
FUNCTION PRIVATE1 (VARIABLE1 IN NUMBER)
RETURN NUMBER;
FUNCTION PRIVATE2 (VARIABLE2 IN NUMBER)
RETURN NUMBER;
PROCEDURE TEST2(VARIABLE IN NUMBER, VARIABLE OUT NUMBER)
BEGIN
......
END;
FUNCTION PRIVATE1 (VARIABLE1 IN NUMBER)
RETURN NUMBER
IS
BEGIN
........
END;
FUNCTION PRIVATE2 (VARIABLE2 IN NUMBER)
RETURN NUMBER
IS
BEGIN
.......
END;
END;
Any suggestions on how I can fix it?
FUNCTION PRIVATE2 (VARIABLE2 IN NUMBER)
RETURN NUMBER; // REMOVE semi colon from here
IS
BEGIN
.......
END;
You are probably using private function in SQL - this is not supported. In order to use function in SQL you have to define it in the package specification.
We need source code of private1 and private2 functions in order to be able to help more.
[Edit] Here is the example of the package that will fail to compile due to using private function in SQL:
create or replace package test_pkg is
end;
/
create or replace package body test_pkg is
function F1 return number is
begin
return 2;
end;
function F2 return number is
lnNumber number;
begin
select F1 -- function declared only in the package body
into lnNumber
from dual;
return lnNumber + 1;
end;
end;
/
PLS-00231: function 'F1' may not be used in SQL
PL/SQL: ORA-00904: "F1": invalid identifier
PL/SQL: SQL Statement ignored
I have a package body as give bellow. I need to call a function named TESTING() inside the function body and call another query. Code is given bellow
CREATE OR REPLACE PACKAGE BODY TEST_PCAKAGE AS -- body
FUNCTION OUTER_FUNCTION (
INPUT_A IN VARCHAR2,
INPUT_B IN DATE,
) RETURN REF_CURSOR_TYPE IS
CUR_CA_RECEIPTS REF_CURSOR_TYPE;
BEGIN
OPEN CUR_CA_RECEIPTS FOR
TESTING();
SELECT * FROM TEST_TABLE;
RETURN CUR_CA_RECEIPTS;
END OUTER_FUNCTION;
END TEST_PCAKAGE ;
when creating this package body, it gives me errors. Can anyone please tell me how to do this?
Error : Error(14,1): PLS-00428: an INTO clause is expected in this SELECT statement
create or replace
FUNCTION "TESTING" RETURN VARCHAR2
AS
BEGIN
RETURN('SUCCESS');
END;
I'm guessing you'r trying to return the cursor from the function
Try this :
CREATE OR REPLACE PACKAGE BODY TEST_PCAKAGE AS -- body
FUNCTION OUTER_FUNCTION (
INPUT_A IN VARCHAR2,
INPUT_B IN DATE,
) RETURN REF_CURSOR_TYPE IS
-- use sys_refcursor for dynamic cursors
CUR_CA_RECEIPTS sys_refcursor;
BEGIN
-- Open the cursor for a query and not a function
OPEN CUR_CA_RECEIPTS FOR SELECT * FROM TEST_TABLE;
-- i'm guessing this is for debug
TESTING();
-- you can now iterate it in a different procedure
RETURN CUR_CA_RECEIPTS;
END OUTER_FUNCTION;
END TEST_PCAKAGE ;
I want to write the following procedure differently so i can call it to return data as if it were a table by doing: SELECT * FROM table(package.get7DayCapacityDemandProv(1, sysdate))
Procesdure:
PROCEDURE get7DayCapacityDemandProv(p_H_id IN work_entity_data.H_id%TYPE
,p_date IN DATE
,p_capacity_day_1 OUT NUMBER
,p_demand_day_1 OUT NUMBER
,p_capacity_day_2 OUT NUMBER
,p_demand_day_2 OUT NUMBER
,p_capacity_day_3 OUT NUMBER
,p_demand_day_3 OUT NUMBER
,p_capacity_day_4 OUT NUMBER
,p_demand_day_4 OUT NUMBER
,p_capacity_day_5 OUT NUMBER
,p_demand_day_5 OUT NUMBER
,p_capacity_day_6 OUT NUMBER
,p_demand_day_6 OUT NUMBER
,p_capacity_day_7 OUT NUMBER
,p_demand_day_7 OUT NUMBER
)
IS
BEGIN
getCapacityDemandOnDayProvider(p_H_id
,p_date
,p_capacity_day_1
,p_demand_day_1
);
getCapacityDemandOnDayProvider(p_H_id
,p_date + 1
,p_capacity_day_2
,p_demand_day_2
);
getCapacityDemandOnDayProvider(p_H_id
,p_date + 2
,p_capacity_day_3
,p_demand_day_3
);
getCapacityDemandOnDayProvider(p_H_id
,p_date + 3
,p_capacity_day_4
,p_demand_day_4
);
getCapacityDemandOnDayProvider(p_H_id
,p_date + 4
,p_capacity_day_5
,p_demand_day_5
);
getCapacityDemandOnDayProvider(p_H_id
,p_date + 5
,p_capacity_day_6
,p_demand_day_6
);
getCapacityDemandOnDayProvider(p_H_id
,p_date + 6
,p_capacity_day_7
,p_demand_day_7
);
END get7DayCapacityDemandProv;
You want to (1) convert this to a function that returns a record, and then (2) convert that to a pipelined function.
Here's an example. I've left out the first parameter just so I could easily run it, but you can just add that back in.
create or replace package test
as
type theRecordType is record (
day date,
capacity number,
demand number
);
type theTableType is table of theRecordType;
function getData(p_date DATE) return theTableType pipelined;
end test;
/
create or replace package body test
as
function getData(p_date DATE) return theTableType pipelined
as
theRecord theRecordType;
begin
for i in 0..6 loop
theRecord.date := p_date + i;
theRecord.capacity := i;
theRecord.demand := i+1;
--
-- you would have a call to your procedure instead of the above two lines
-- getCapacityDemandOnDayProvider(p_H_id
-- ,theRecord.date
-- ,theRecord.capacity
-- ,theRecord.demand
-- );
--
pipe row (theRecord);
end loop;
return;
end getData;
end test;
/
You can now select from the function and get one row for each day.
select * from table(test.getData(SYSDATE));
I made it a package so the types could be declared within the package header. Alternatively, you could keep it a standalone function and declare the types within the schema using CREATE TYPE.
This is off the cuff, so it is not going to be 100% syntactically correct, but it will be conceptually correct.
create or replace package bingo IS
TYPE bingoCursor is REF CURSOR;
function get7Days(
bingoId IN bingoTable.bingoId%TYPE,
bingoDate IN date)
return bingoCursor;
end bingo;
create or replace package body bingo IS
function get7Days(
bingoId IN bingoTable.bingoId%TYPE,
bingoDate IN date)
return bingoCursor IS
sevenDaysContent bingoCursor;
begin
open sevenDaysContent for
select day 1 stuff;
union all
select day 2 stuff;
union all
... select and union all days 3 - day 7;
return sevenDaysContent;
end get7Days;
end bingo;
Sounds like you want a function or a view. A procedure's returned data can be caught and used within a SQL script but it requires dumping it into a temp or variable table first.
If your requirement is to be able to just do a SELECT * FROM something then you probably want a function or a view.
You could create a function that returns a sys_refcursor. An overly simple example:
create or replace function BLAH(somevar in varchar2) return sys_refcursor IS
v_result_cur sys_refcursor;
v_query varchar2(4000);
...
begin
...
v_query := 'select field1, field2 from blah';
...
open v_result_cur for v_query;
return v_result_cur;
...
exception
...
end;
But I will say this isn't typical. I would probably use a view (or materialized view), or turn those inner procedures into functions and simply select them, like:
select
FN_getCapacityDemandOnDayProvider(someVars) as Day1Val,
FN_getCapacityDemandOnDayProvider(someVars2) as Day2Val
from dual;