Oracle - no function with name X exists in this scope - oracle

The function is clearly there, because I can navigate to it using SQL Developer and it compiles all fine, but when I try to use the function with or without "call", it throws:
Error(36,24): PLS-00222: no function with name 'x' exists in this
scope
This is how the function looks like:
create or replace function testfunction
(
somevalue in varchar2
)
return varchar2
AS
cursor testcursor IS
select column1, column2 from table1 t
where t.column1 = somevalue;
testcursorrec testcursor %rowtype;
messaget VARCHAR2(500);
begin
open testcursor ;
fetch testcursor into testcursorrec ;
close testcursor ;
messaget := testcursor.column1;
return messaget ;
end;
This is how I'm calling it:
messaget := testfunction(somevalue);
where both messageT and somevalue are declared as varchar2 type.
Are cursors not allowed inside function or something like that?

the error would be messaget := testcursor.column1; as the cursor is closed by then (you should just use testcursorrec.column2.
you're code isn't checking for no rows, nor duplicate rows. you can simplify this to
create or replace function testfunction
(
somevalue in table1.column1%type
)
return table1.column2%type
AS
messaget table1.column2%type; -- use %type where possible.
begin
select t.column2
into messaget
from table1 t
where t.column1 = somevalue
and rownum = 1;--only if you dont care if theres 2+ rows.
return messaget;
exception
when no_data_found
then
return null; -- if you want to ignore no rows.
end;

Related

Return updated rows from a stored function

Im trying to select some rows from a Table in ORACLE and at the same time update the selected rows state. I found a way to do so with a stored function and Cursors but I cant manage to return the rows after using the cursor to update. This is my code:
CREATE OR REPLACE FUNCTION FUNCTION_NAME
RETURN SYS_REFCURSOR
IS
l_return SYS_REFCURSOR;
CURSOR c_operations IS
SELECT * FROM TABLE1
WHERE STATUS != 'OK'
FOR UPDATE OF TABLE1.STATUS;
BEGIN
FOR r_operation IN c_operations
LOOP
UPDATE
TABLE1
SET
TABLE1.STATUS = 'OK'
WHERE
TABLE1.ID_TABLE1 = r_operation.ID_TABLE1;
END LOOP;
COMMIT;
-- Missing conversion from cursor to sys_refcursor
RETURN l_return;
END;
The update is working but Im still missing how to return the updated rows that are in the cursor (c_operations ).
Thank you.
I'm going to make some assumptions:
id_table1 is the primary key of the table, so your RBAR (*) update affects only one row
id_table1 is numeric
If these assumptions are wrong you will need to tweak the following code.
CREATE OR REPLACE FUNCTION FUNCTION_NAME
RETURN SYS_REFCURSOR
IS
l_return SYS_REFCURSOR;
l_id table1.id_table1%type;
l_upd_ids sys.odcinumberlist := new sys.odcinumberlist();
CURSOR c_operations IS
SELECT * FROM TABLE1
WHERE STATUS != 'OK'
FOR UPDATE OF TABLE1.STATUS;
BEGIN
FOR r_operation IN c_operations LOOP
UPDATE TABLE1
SET TABLE1.STATUS = 'OK'
WHERE TABLE1.ID_TABLE1 = r_operation.ID_TABLE1
returning TABLE1.ID_TABLE1 into l_id;
l_upd_ids.extend();
l_upd_ids(l_upd_ids.count()) := l_id;
END LOOP;
COMMIT;
open l_return for
select * from table(l_upd_ids);
RETURN l_return;
END;
The key points of the solution.
uses Oracle maintained collection (of number) sys.odcinumberlist to store the updated IDs;
uses RETURNING clause to capture the id_table1 value for the updated row;
stores the returned key in the collection;
uses a table() function to casrt the collection into a table which can be queried in the ref cursor.
This last point is why I chose to use sys.odcinumberlist rather than defining a collection in the procedure. It's a SQL type, so we can use it in SELECT statements.
(*) Row-by-agonizing-row. Updating single records in a PL/SQL loop is the slowest way of executing bulk updates, and normally constitutes an anti-pattern. A straightforward set-based UPDATE should suffice. However, you know your own situation so I'm going to leave that as it is.
It looks to me like you don't need the initial cursor, since you're changing the STATUS of every row which is not 'OK' to 'OK', so you can do this is a simple UPDATE statement. Then use an OPEN...FOR statement to return a cursor of all rows where STATUS is not 'OK', which shouldn't return anything because you've already changed all the status values to 'OK'. I suggest that you rewrite your procedure as:
CREATE OR REPLACE FUNCTION FUNCTION_NAME
RETURN SYS_REFCURSOR
IS
l_return SYS_REFCURSOR;
BEGIN
UPDATE TABLE1
SET STATUS = 'OK'
WHERE STATUS != 'OK';
COMMIT;
OPEN l_return FOR SELECT *
FROM TABLE1
WHERE STATUS != 'OK'
FOR UPDATE OF TABLE1.STATUS;
RETURN l_return;
END;
Instead of a loop to update how about a bulk update collecting the updated ids. Then a table function from those returned ids.
create type t_table1_id is
table of integer;
create or replace function set_table1_status_ok
return sys_refcursor
is
l_results_cursor sys_refcursor;
l_updated_ids t_table1_id;
begin
update table1
set status = 'Ok'
where status != 'Ok'
returning table1.id
bulk collect
into l_updated_ids;
open l_results_cursor for
select *
from table1
where id in (select * from table(l_updated_ids));
return l_results_cursor;
end set_table1_status_ok;
-- test
declare
updated_ids sys_refcursor;
l_this_rec table1%rowtype;
begin
updated_ids := set_table1_status_ok();
loop
fetch updated_ids into l_this_rec;
exit when updated_ids%notfound;
dbms_output.put_line ( l_this_rec.id || ' updated.');
end loop;
close updated_ids;
end ;

How to count rows from two columns?

I try to create the function login that takes customer number(pnr) and password from same table. Its fine to create function but test crashes with following eror:
ORA-00904: "P_PASSWD": invalid identifier
create or replace function logga_in(
p_pnr bankkund.pnr%type,
p_passwd bankkund.passwd%type
)
return number
as
v_resultat number(1);
begin
select count(pnr) into v_resultat
from bankkund
where p_pnr = pnr
and p_passwd = passwd;
return 1;
exception
when no_data_found then
return 0;
end;
There is one other problem with your code not suggested in the comments, A count function from a select into will not raise a NO_DATA_FOUND exception. You may use an IF condition on count or do something like this, which is preferable
CREATE OR REPLACE FUNCTION logga_in (
p_pnr bankkund.pnr%TYPE,
p_passwd bankkund.passwd%TYPE
) RETURN NUMBER AS
v_resultat NUMBER(1);
BEGIN
SELECT 1 --do not use count if you wish to handle no_data_found
INTO v_resultat FROM
bankkund WHERE pnr = p_pnr AND
passwd = p_passwd
AND ROWNUM = 1; --Add this
RETURN 1;
EXCEPTION
WHEN no_data_found THEN
RETURN 0;
END;
Now, as far as calling the procedure is concerned, there are various options available including using bind variable
VARIABLE p_pnr number --use the datatype of bankkund.pnr%TYPE
VARIABLE p_passwd VARCHAR2(10) --use the datatype of bankkund.passwd
SELECT logga_in(:p_pnr,:p_passwd) FROM dual;
Or substitution variable
SELECT logga_in('&p_pnr','&p_passwd') FROM dual;
Give inputs when prompted.
Or use PL/SQL block
DECLARE
v_res INT;
v_pnr bankkund.pnr%type := 12892; --or appropriate value
p_passwd bankkund.passwd%type := some_passwd';
BEGIN
v_res := logga_in();
If v_res = 1 THEN
do_something_u_want; --call or execute appropriate action.
END IF;
END;
/

Oracle Where with variable

I have problem with my plsql code. It's just part of my whole job.
declare
id number(2):=1; (here is function which returns any value)
check VARCHAR2(100);
begin
select COUNT(*) into check from T_SDSN_LOG Where ANY_ID=id AND CHECK LIKE
'NAME';
dbms_output.put_line(check);
end;
In this case, my select returns 0 althought it should be 2.
If I change the part
Where ANY_ID=id to
Where ANY_ID=2 it works perfectly. Any advices? I need id to be variable as a return value from function.
This uses a locally defined function so it isn't available in the SQL but can be referenced in the PL/SQL.
DECLARE
lnum_id NUMBER := return_id;
lnum_check VARCHAR2(100);
FUNCTION return_id
RETURN NUMBER
IS
BEGIN
RETURN 123456;
END;
BEGIN
lnum_id := return_id;
SELECT COUNT(*)
INTO lnum_check
FROM my_table
WHERE table_id = lnum_id;
DBMS_OUTPUT.put_line(lnum_check);
END;
You will presumably want this functionality in a package in which case you can declare the function in the package header, write the code for the function in the body and then reference it in the SQL itself. So if I declare a function (FNC_RETURN_ID) in a package called PKG_DATA_CHECKS that returns a NUMBER I can do the following;
DECLARE
lnum_id NUMBER;
lnum_check VARCHAR2(100);
BEGIN
SELECT COUNT(*)
INTO lnum_check
FROM my_table
WHERE table_id = (SELECT pkg_data_checks.fnc_return_id FROM dual);
DBMS_OUTPUT.put_line(lnum_check);
END;

How to pass multiple values for a parameter in a PIPELINED table function in oracle

I have created a function in Oracle with 2 parameters. So when I run the query I want to pass multiple values to each parameter.
I tried using below changes in the query:
coul_1 in ('||par1||') and colu_2 in ('||par2||')
But it is not fetching the data.
How to fetch the data when I give multiple values to different declared parameters.Eg:
select * from table(fun_name('val1','val2'))
val1 will have a1,a2,a3
val2 will have b1,b2,b3
Here is the function code:
CREATE OR REPLACE FUNCTION JOBRUN_STATUS_MONITOR_F(
own_name IN VARCHAR2,
status IN VARCHAR2)
RETURN JOBRUN_STATUS_EDW_1 PIPELINED
IS
L_TAB JOBRUN_STATUS_MONITOR_EDW_1;
JR_STATUS NUMBER (38);
CURSOR jobrun_1_cr (OW_N VARCHAR2, STS VARCHAR2)
IS
SELECT *
FROM JOBRUN A,
JOBMST B,
owner C
WHERE A.JOBMST_ID = B.JOBMST_ID
AND C.OWNER_NAME = OW_N
AND A.JOBRUN_STATUS = STS ;
BEGIN V_OWN_NAME := own_name;
V_STATUS := status;
IF jobrun_1_cr%ISOPEN THEN
CLOSE jobrun_1_cr;
END IF;
OPEN jobrun_1_cr (own_name, JR_STATUS);
CLOSE jobrun_1_cr;
END JOBRUN_STATUS_MONITOR_F;
/
It sounds like you want to call the function three times, passing (a1, b1), (a2, b2) and (a3, b3). One way would be to generate an inline view containing the values you want to pass, and query it including a call to your function.
Demo pipelined function:
create or replace function demo_pipefunc
( p_own_name in varchar2
, p_status in varchar2 )
return sys.dbms_debug_vc2coll
pipelined
as
l_result long;
begin
for i in 1..3 loop
l_result := p_own_name ||';'|| p_status ||';'|| i;
pipe row (l_result);
end loop;
return;
end demo_pipefunc;
Demo call:
with params (own_name, status) as
( select 'a1', 'b1' from dual union all
select 'a2', 'b2' from dual union all
select 'a3', 'b3' from dual
)
select t.*
from params
cross join table(demo_pipefunc(own_name, status)) t
Output:
COLUMN_VALUE
a1;b1;1
a1;b1;2
a1;b1;3
a2;b2;1
a2;b2;2
a2;b2;3
a3;b3;1
a3;b3;2
a3;b3;3
try this:
CREATE OR REPLACE FUNCTION JOBRUN_STATUS_MONITOR_F (own_name IN VARCHAR2, status IN VARCHAR2) RETURN JOBRUN_STATUS_EDW_1 PIPELINED
IS L_TAB JOBRUN_STATUS_MONITOR_EDW_1;
JR_STATUS NUMBER (38);
CURSOR jobrun_1_cr (OW_N VARCHAR2, STS VARCHAR2)
IS SELECT *
FROM JOBRUN A, JOBMST B, owner C
WHERE A.JOBMST_ID = B.JOBMST_ID
AND C.OWNER_NAME = OW_N
AND A.JOBRUN_STATUS = STS;
BEGIN
V_OWN_NAME := REPLACE(own_name,'"','''');
V_STATUS := REPLACE(status,'"','''');
IF jobrun_1_cr%ISOPEN THEN
CLOSE jobrun_1_cr;
END IF;
OPEN jobrun_1_cr (own_name, JR_STATUS);
CLOSE jobrun_1_cr;
END JOBRUN_STATUS_MONITOR_F;
then call the procedure like this:
select * from table(fun_name('"a1","b1","c1"','"a1","b1","c1"'));
Your procedure may have other issues with the use of undeclared variables.

PL/SQL: varchar2 as IN parameter to function not working

I have a PL/SQL function like this:
FUNCTION find_by_items(items IN varchar2)
RETURN SYS_REFCURSOR
AS
o_cursor SYS_REFCURSOR;
BEGIN
open o_cursor for select a.something from my_table a where a.item in (items);
return o_cursor;
END find_by_items;
This always returns an empty cursor.
If I run it with, for example:
select find_by_items('''one'', ''two'', ''three''');
I get an empty cursor.
If I put the sql statement in a string and revise the function like:
FUNCTION find_by_items(items IN varchar2)
RETURN SYS_REFCURSOR
AS
o_cursor SYS_REFCURSOR;
strSql varchar2(32767);
BEGIN
strSql := 'select a.something from my_table a where a.item in (' || items || ')';
open o_cursor for strSql;
return o_cursor;
END find_by_items;
And I call the function the exact same way:
select find_by_items('''one'', ''two'', ''three''');
I get the results I expect (not an empty cursor)
--
Basically I'm trying to figure out how to make the first way work, because using a string means that it's more prone to runtime errors rather than compile errors - which in my particular case is not preferable.
Thanks
The first implementation will never work as you expect, because Oracle resolves the Query as follows:
select a.something from my_table a where a.item in ('''one'', ''two'', ''three''')
and what your want is
select a.something from my_table a where a.item in ('one', 'two','three')
split and store your string parameter values into a varray so you can use the following form:
select a.something from my_table a where a.item in (table(varray_var));

Resources