pl/sql table function with weak ref cursor - sql

I need to return from table function string like inside ref cursor;
create or replace
FUNCTION get_data(QUERY in VARCHAR2)
RETURN [SOMETHING] pipelined
is
ret sys_refcursor;
BEGIN
open ret for QUERY;
Loop
fetch ret into [SOMETHING];
exit when ret%notfound;
pipe row(str);
end loop;
close ret;
END get_data;
Any idea, how I can return type like ret%rowtype.

declare
type auth_cursor is ref cursor
return employees%rowtype;
c2 auth_cursor;
r_c2 c2%rowtype;
function get_auth return auth_cursor
is
c1 auth_cursor;
begin
open c1 for select * from employees;
return c1;
end;
begin
c2 := get_auth;
loop
fetch c2 into r_c2;
exit when c2%notfound;
dbms_output.put_line(initcap(
r_c2.last_name));
end loop;
close c2;
end;

Related

PLSQL: using subquery in if-statement error PLS-00405

I'm getting error pls-00405 when I try to run this code:
BEGIN
IF :P10_KAART_CODE IN (SELECT KAART_CODE FROM CADEAUKAART) THEN
RETURN TRUE;
ELSE
RETURN FALSE;
END IF;
END;
There are some similar questions about this but couldn't find a solution for this simple code. Is there another way of writing this without facing a error?
PL/SQL doesn't support embedded SQL in if statements. So you'll need to rewrite your code like this:
create or replace function validate_kaart_code
(P10_KAART_CODE in CADEAUKAART.KAART_CODE%type)
return boolean
is
rv boolean;
l_code CADEAUKAART.KAART_CODE%type;
BEGIN
begin
SELECT KAART_CODE into l_code
FROM CADEAUKAART
where KAART_CODE =:P10_KAART_CODE
and rownum = 1 -- only necessary if KAART_CODE is not unique
;
rv := TRUE;
exception
when no_data_found then
rv := FALSE;
end;
RETURN rv;
END;
I have attempted to reconstruct your whole functionality from the snippet you posted. If this isn't what you intended and you can't convert it to fit your needs please provide more details.
I'd move the condition to the query itself, and catch a NO_DATA_FOUND exception:
BEGIN
SELECT * FROM CADEAUKAART WHERE kaart_code = :P10_KAART_CODE;
RETURN TRUE;
EXCEPTION WHEN NO_DATA_FOUND THEN
RETURN FALSE;
END;
One more way:
CREATE OR REPLACE FUNCTION IS_KAART_CODE_VALID(pinKAART_CODE IN CADEAUKAART.KAART_CODE%TYPE)
RETURN BOOLEAN
IS
nCount NUMBER;
BEGIN
SELECT COUNT(*)
INTO nCount
FROM CADEAUKAART
WHERE KAART_CODE = pinKAART_CODE ;
RETURN CASE
WHEN nCount > 0 THEN
TRUE
ELSE
FALSE
END;
END IS_KAART_CODE_VALID;
You may try a cursor, alternatively :
DECLARE
v_flag boolean := FALSE;
BEGIN
FOR c IN ( SELECT KAART_CODE FROM CADEAUKAART )
LOOP
IF :P10_KAART_CODE = c.KAART_CODE THEN
v_flag := TRUE;
EXIT;
END IF;
EXIT WHEN NO_DATA_FOUND;
END LOOP;
RETURN v_flag;
END;
By your way using a select statement is not allowed, you might list all the members for the returning values of KAART_CODE such as
IF :P10_KAART_CODE IN ('aAA','BBb','ccC'..) THEN
but which is not preferable and nice to list all matching values .

Oracle PL/SQL: Function Error when passing parameters

When I pass a parameter to a function call, I get the following error:
Error: PLS-00306: wrong number or types of arguments in call to
'GET_NUM'.
The code is as follows:
CREATE OR REPLACE PACKAGE BODY TESTJNSABC IS
-- FUNCTION IMPLEMENTATIONS
FUNCTION get_num(num IN NUMBER)
RETURN VARCHAR2 IS
my_cursor VARCHAR2(20);
BEGIN
IF get_num = 1 THEN
my_cursor:= 'hello world';
ELSE
my_cursor:= 'Hi!';
END IF;
RETURN my_cursor;
END;
-- PROCEDURE IMPLEMENTATIONS
PROCEDURE testingabc AS
x NUMBER(3);
BEGIN
x:= 2;
dbms_output.put_line(get_num(x));
END testingabc;
END TESTJNSABC;
You have an issue in IF get_num = 1 THEN, because you are calling the function get_num without parameters, while it has one input parameter
If you want to check the parameter value, you probably mean:
IF num = 1 THEN
The problem is at IF get_num = 1. The code will work after you change it to IF num = 1.
Entire sample code below:
CREATE OR REPLACE PACKAGE BODY TESTJNSABC IS
-- FUNCTION IMPLEMENTATIONS
FUNCTION get_num(num IN NUMBER)
RETURN VARCHAR2 IS
my_cursor VARCHAR2(20);
BEGIN
IF num = 1 THEN
my_cursor:= 'hello world';
ELSE
my_cursor:= 'Hi!';
END IF;
RETURN my_cursor;
END;
-- PROCEDURE IMPLEMENTATIONS
PROCEDURE testingabc AS
x NUMBER(3);
BEGIN
x:= 2;
dbms_output.put_line(get_num(x));
END testingabc;
END TESTJNSABC;

Reference to Unintialized collection exception using array PLSQL

I am getting reference to uninitialized collection exception when trying to add element to an array. Please help.Is that correct way to initialize the array as mentioned in my calling code?
CREATE OR REPLACE TYPE SCHEMA.STRARRAY AS TABLE OF VARCHAR2 (255);
CREATE OR REPLACE PROCEDURE SCHEMA.PR_VALIDATE
(
FILEARRAY IN STRARRAY,
DUPARRAY OUT STRARRAY)
IS
dupCount NUMBER;
fileName VARCHAR2 (50);
fileId NUMBER;
dupfileName VARCHAR2(50);
BEGIN
for i in 1 .. FILEARRAY.count
loop
fileName := FILEARRAY(i);
SELECT COUNT (T.FILEID), T.FILEID INTO dupCount,fileId FROM TB_COMPANY T, TB_COMPANY S
WHERE T.FILEID=S.FILEID
AND T.RPDT_ORI_FLE_NM = fileName AND T.RPDT_STA_CD IN ('PASS', 'FAIL')
GROUP BY T.FILEID;
IF dupCount>1
THEN
SELECT RPDT_ORI_FLE_NM INTO dupfileName FROM TB_RDTE_COMPANY_HDR_DT
WHERE RPDT_STA_CD IN ('PR15','PR16') AND RPDT_FLE_ID=fileId
AND RPDT_ORI_FLE_NM != fileName;
DBMS_OUTPUT.PUT_LINE(dupfileName);
DUPARRAY(DUPARRAY.LAST +1 ) :=dupfileName; --Here is the exception.
END IF;
end loop;
EXCEPTION
WHEN OTHERS THEN
PR_RDTE_ERRORS('PR_VALIDATE', SQLERRM);
ROLLBACK;
END;
/
Calling code:
DECLARE
DUPARRAY STRARRAY:=STRARRAY();
BEGIN
PR_VALIDATE (STRARRAY('abc.txt'),DUPARRAY);
END;
DUPARRAY.LAST will get the index of the last element of the array (and not the index of the last non-NULL value of the array) - so if you use DUPARRAY.LAST + 1 you will always exceed the array bounds.
Also, you have not extended the array - which you need to do to add an extra element.
You need to do:
DUPARRAY.EXTEND;
DUPARRAY(DUPARRAY.LAST) :=dupfileName;
You also need to initialise the DUPARRRY inside the procedure (instead of in the calling code):
So something like this:
CREATE OR REPLACE PROCEDURE PR_VALIDATE
(
FILEARRAY IN STRARRAY,
DUPARRAY OUT STRARRAY
)
IS
dupCount NUMBER;
fileName VARCHAR2 (50);
dupfileName VARCHAR2(50);
fileId NUMBER;
BEGIN
DUPARRAY := STRARRAY();
for i in 1 .. FILEARRAY.count loop
fileName := FILEARRAY(i);
SELECT COUNT (T.FILEID), T.FILEID
INTO dupCount,fileId
FROM TB_COMPANY T
INNER JOIN TB_COMPANY S
ON T.FILEID=S.FILEID
WHERE T.RPDT_ORI_FLE_NM = fileName
AND T.RPDT_STA_CD IN ('PASS', 'FAIL')
GROUP BY T.FILEID;
IF dupCount>1 THEN
SELECT RPDT_ORI_FLE_NM
INTO dupfileName
FROM TB_RDTE_COMPANY_HDR_DT
WHERE RPDT_STA_CD IN ('PR15','PR16')
AND RPDT_FLE_ID=fileId
AND RPDT_ORI_FLE_NM != fileName;
DBMS_OUTPUT.PUT_LINE(dupfileName);
DUPARRAY.EXTEND;
DUPARRAY(DUPARRAY.LAST) :=dupfileName;
END IF;
end loop;
END;
/
Then call it:
DECLARE
DUPARRAY STRARRAY;
BEGIN
PR_VALIDATE(STRARRAY('abc.txt'),DUPARRAY);
END;
/

PL sql to find a string in a list

I have a problem because in this code I don't know how I can to test if a string is in a list which isn't initialized .
The code is this :
TYPE t1 IS TABLE OF VARCHAR2(32767) index by PLS_INTEGER;
v_t1 t1;
WOUT varchar2(80) :='bbbb';
v_t1(1):='bbbb';
if (WOUT member of v_t1 ) then
....
end if;
I don't know how i can write in plsql the condition that wout is inside v_t1 because member is accepted only with lists initialized.
Thanks for the help
With the collection declared as you have it there's no simple way to do what you're trying to do except to iterate through the collection to find the element you want:
declare
TYPE t1 IS TABLE OF VARCHAR2(32767)
index by PLS_INTEGER;
v_t1 t1;
WOUT VARCHAR2(80) := 'zzzz';
i NUMBER;
bFound BOOLEAN := FALSE;
BEGIN
v_t1(0) := 'aaaa';
v_t1(1) := 'bbbb';
v_t1(2) := 'cccc';
v_t1(26) := 'zzzz';
i := v_t1.FIRST;
LOOP
DBMS_OUTPUT.PUT_LINE('i=' || i);
IF v_t1(i) = WOUT THEN
bFound := TRUE;
EXIT;
END IF;
i := v_t1.NEXT(i);
IF i IS NULL THEN
EXIT;
END IF;
END LOOP;
DBMS_OUTPUT.PUT_LINE('bFound=' || CASE WHEN bFOUND THEN 'TRUE' ELSE 'FALSE' END);
end;
HOWEVER - if you change the collection so that it's indexed by the string it contains you can accomplish this task a bit easier by using the EXISTS collection method:
declare
TYPE T2 IS TABLE OF VARCHAR2(32767)
INDEX BY VARCHAR2(32767);
v_t2 t2;
WOUT VARCHAR2(80) := 'zzzz';
i NUMBER;
bFound BOOLEAN;
BEGIN
v_t2('aaaa') := 'aaaa';
v_t2('bbbb') := 'bbbb';
v_t2('cccc') := 'cccc';
v_t2('zzzz') := 'zzzz';
bFound := v_t2.EXISTS(WOUT);
DBMS_OUTPUT.PUT_LINE('bFound=' || CASE WHEN bFOUND THEN 'TRUE' ELSE 'FALSE' END);
END;
The documentation on collection methods can be found here.
Share and enjoy.
You can create special function that checks if such value is inside collection. Something like that:
FUNCTION exist_in_collection( p_table t1, p_text VARCHAR2) RETURN boolean
IS
BEGIN
IF p_table.LAST IS NULL THEN
RETURN FALSE;
ELSE
FOR i IN p_table.FIRST.. p_table.LAST
LOOP
IF p_table(i) = p_text THEN
RETURN TRUE;
END IF;
END LOOP;
RETURN FALSE;
END IF;
END exist_in_collection;
if p_table is not initialized LAST is NULL you will get FALSE. And then you can use this function to check whether the string is inside collection:
IF exist_in_collection(p_table, WOUT) THEN ... END IF;

Skip invalid cursor and continue loop

I have the below procedure were the loop is breaking if the cursor does not have any record in it. I would like to contine the iteration even if the cursor does not have any record.
My procedure is,
CREATE OR REPLACE PROCEDURE "MAPSADMIN"."FORECAST_MAINTENANCE_SCH" (
in_carrierCode VARCHAR2,
in_WindowPeriodStr VARCHAR2,
out_forecastRecords OUT types.cursortype
)
IS
vtailAndCheckCur types.cursortype;
vForecastRecsCursor types.cursortype;
vtailNo VARCHAR2(10);
vmaintCheckType VARCHAR2(10);
vforcastRecords forecastObjectsList := forecastObjectsList();
forcastRec forecastObjectsList := forecastObjectsList();
BEGIN
dbms_output.enable(null);
OPEN vtailAndCheckCur FOR
select td.tail_number,mpm.maint_check_type
from tail_details td, maint_plan_tail_map mptm, maint_plan_master mpm
where td.tail_number = mptm.tail_number
and mpm.maint_plan_code = mptm.maint_plan_code
and mpm.carrier_code = in_carrierCode
UNION
select td.tail_number,mpm.maint_check_type
from tail_details td, maint_plan_subfleet_map mptm, maint_plan_master mpm
where td.subfleet_code = mptm.subfleet_code
and mpm.maint_plan_code = mptm.maint_plan_code
and mpm.carrier_code = in_carrierCode;
LOOP
FETCH vtailAndCheckCur INTO vtailNo, vmaintCheckType;
dbms_output.put_line( vtailNo||' '||vmaintCheckType );
FORECAST_OBJS_LIST(vtailNo,vmaintCheckType,in_WindowPeriodStr,vForecastRecsCursor);
LOOP
forcastRec.EXTEND;
forcastRec(forcastRec.COUNT) := FORECASTOBJECT(null,null,null,null,null,null,null,null,null,null);
dbms_output.put_line( 'test');
FETCH vForecastRecsCursor INTO
forcastRec(forcastRec.COUNT).maintNextPlanCode,
forcastRec(forcastRec.COUNT).tailNumber,
forcastRec(forcastRec.COUNT).maintNextCheckType,
forcastRec(forcastRec.COUNT).maintNextCycleCheckLabel,
forcastRec(forcastRec.COUNT).maintNextStartDate,
forcastRec(forcastRec.COUNT).maintNextEndDate,
forcastRec(forcastRec.COUNT).maintNextDueDate,
forcastRec(forcastRec.COUNT).maintNextCalendarDays,
forcastRec(forcastRec.COUNT).maintForecastFactor,
forcastRec(forcastRec.COUNT).maintCheckColor;
dbms_output.put_line( forcastRec(forcastRec.COUNT).maintNextPlanCode);
EXIT WHEN vForecastRecsCursor%NOTFOUND;
END LOOP;
EXIT WHEN vtailAndCheckCur%NOTFOUND;
END LOOP;
CLOSE vtailAndCheckCur;
OPEN out_forecastRecords FOR
SELECT tailNumber,maintNextCheckType,maintNextPlanCode,maintNextCycleCheckLabel,maintNextStartDate,
maintNextEndDate,maintNextDueDate,maintNextCalendarDays,maintForecastFactor,maintCheckColor
FROM TABLE(CAST(forcastRec AS forecastObjectsList));
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(DBMS_UTILITY.FORMAT_ERROR_STACK);
END FORECAST_MAINTENANCE_SCH;
/
Here if vForecastRecsCursor returns without 0 records in it i am getting ORA-01001: invalid cursor. Anyone please explain how to iterate further by ignoring the error.
Thanks in advance
Try to check if vForecastRecsCursor is opened:
IF vForecastRecsCursor%ISOPEN THEN
...
END IF;
You can control the loop by the GOTO clause or using blocks of code.
DECLARE
dummy_cur SYS_REFCURSOR;
l_dummy_num NUMBER;
CURSOR dual_cur IS
SELECT 1
FROM dual
WHERE 1 = 2;
l_dual_num NUMBER;
l_counter NUMBER := 0;
BEGIN
OPEN dummy_cur FOR SELECT 1 FROM dual;
LOOP
FETCH dummy_cur INTO l_dummy_num;
IF dummy_cur%NOTFOUND THEN
GOTO exit_command;
END IF;
-- inner cursor
BEGIN
-- open inner cursor
IF l_counter = 0 THEN
OPEN dual_cur;
END IF;
-- loop inner cursor
LOOP
FETCH dual_cur INTO l_dual_num;
DBMS_OUTPUT.PUT_LINE('inner loop');
EXIT WHEN dual_cur%NOTFOUND;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('exception');
END;
<<exit_command>>
EXIT WHEN dummy_cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('l_dummy_num: ' || l_dummy_num);
l_counter := l_counter + 1;
DBMS_OUTPUT.PUT_LINE('l_counter: ' || l_counter);
END LOOP;
--close inner cursor
CLOSE dual_cur;
CLOSE dummy_cur;
EXCEPTION
WHEN OTHERS THEN
CLOSE dual_cur;
CLOSE dummy_cur;
END;