converting a plsql code into a function - oracle

I have this piece of code which I want to convert into a function , and call the function in a select statement by passing vendor_site_id from ap_supplier_sites_all. so that I can get the value of vendor_number i.e segment1 of ap_suppliers . here in this piece of code , I have a login to fetch first 6 digits from the column attribute52 of DFF table gecm_dff_ext .
DECLARE
v_ret_val VARCHAR2(30);
v_sup_gsl VARCHAR2(30);
v_vendor_id NUMBER;
BEGIN
v_vendor_id := '${PO.H_VENDOR_ID}';--> vendor_id column from ap_suppliers
IF <condition> = 'Y'
THEN
BEGIN
SELECT SUBSTR(ATTRIBUTE52,1,6)
INTO v_sup_gsl
FROM gecm_dff_ext
WHERE primary_table ='AP_SUPPLIER_SITES_ALL'
AND primary_key = '${PO.H_VENDOR_SITE_ID}';--> This value should be vendor_site_id from ap_supplier_sites_all table
EXCEPTION
WHEN OTHERS THEN
v_sup_gsl := NULL;
END;
BEGIN
IF v_sup_gsl IS NOT NULL THEN
SELECT segment1
INTO v_ret_val
FROM ap_suppliers
WHERE segment1 = v_sup_gsl;
END IF;
EXCEPTION
WHEN OTHERS THEN
v_ret_val := '';
END;
END IF;
IF v_sup_gsl IS NULL THEN
BEGIN
SELECT SEGMENT1
INTO v_ret_val
FROM ap_suppliers
WHERE vendor_id=v_vendor_id;
EXCEPTION
WHEN OTHERS THEN
v_ret_val := '';
END;
END IF;
:return_value:=v_ret_val;
END;

This is a simplified example: the following SELECT returns department name for a certain department number:
SQL> select dname
2 from dept
3 where deptno = &par_deptno;
Enter value for par_deptno: 10
DNAME
--------------
ACCOUNTING
SQL>
So, how to convert it to a function? By using a proper syntax, declaring a return variable, fetching into it and - returning the result:
SQL> create or replace function f_test (par_deptno in dept.deptno%type)
2 return dept.dname%type
3 is
4 retval dept.dname%type;
5 begin
6 select dname
7 into retval
8 from dept
9 where deptno = par_deptno;
10
11 return retval;
12 exception
13 when no_data_found then
14 return null;
15 end;
16 /
Function created.
SQL> select f_test(10) from dual;
F_TEST(10)
---------------------------------------------------------------------------
ACCOUNTING
SQL> select f_test(999) from dual;
F_TEST(999)
---------------------------------------------------------------------------
SQL>
This is what you should do. It seems that you are returning the v_ret_val, so - your code might look like this:
create or replace function f_test (par_vendor_id po.h_vendor_id%type)
return ap_suppliers.segment1%type
is
v_ret_val ap_suppliers.segment1%type;
begin
if <condition> = 'Y' then
...
end if;
return v_ret_val;
end;

Related

SELECT INTO variable not working in PL\SQL function

Not sure why this isn't working, when the base query (the commented out line) does return a value when I pass in the same id - when executed as a straight query outside the function.
Maybe I need to use execute immediate, but not sure why this is returning a "no data" error. I got it working with execute immediate, but interested to know what the issue is with the syntax below.
create or replace FUNCTION "FN_GET_CSP_TEMPLATE_DETAILS" (
TEMPLATE_USED_ID IN VARCHAR2 DEFAULT NULL
)
RETURN VARCHAR2 AS
v_out VARCHAR2(100);
BEGIN
dbms_output.put_line(TEMPLATE_USED_ID);
SELECT i_chronicle_id INTO v_out from TABLE_A where R_OBJECT_ID = TEMPLATE_USED_ID;
-- // this works // SELECT i_chronicle_id INTO v_out from TABLE_A where R_OBJECT_ID = 'ID_99';
dbms_output.put_line(v_out);
return v_out;
end;
So this returns a count of 1:
select count(*) from TABLE_A WHERE R_OBJECT_ID = 'ID_99';
But this returns a null value, function defined as above:
select FN_GET_CSP_TEMPLATE_DETAILS('ID_99') from dual;
This works fine:
create or replace FUNCTION "FN_GET_CSP_TEMPLATE_DETAILS" (
TEMPLATE_USED_ID IN VARCHAR2 DEFAULT NULL
)
RETURN VARCHAR2 AS
v_out VARCHAR2(100);
sql_stmt VARCHAR2(1000);
BEGIN
dbms_output.put_line(TEMPLATE_USED_ID);
sql_stmt := 'SELECT i_chronicle_id from TABLE_A where R_OBJECT_ID = :a';
EXECUTE IMMEDIATE sql_stmt into v_out using TEMPLATE_USED_ID;
dbms_output.put_line(v_out);
return v_out;
end;
Seems it only doesnt work against this production table. If I recreate a dummy table and a function against the dummy table it works.
CREATE TABLE "ANALYTICS"."TEST_CSP_FUNCTION"
( "R_OBJECT_ID" VARCHAR2(20 BYTE),
"I_CHRONICLE_ID" VARCHAR2(20 BYTE)
)
INSERT INTO TEST_CSP_FUNCTION
(R_OBJECT_ID, I_CHRONICLE_ID)
VALUES
('ID_100', 'Doc A');
INSERT INTO TEST_CSP_FUNCTION
(R_OBJECT_ID, I_CHRONICLE_ID)
VALUES
('ID_101', 'Doc B');
INSERT INTO TEST_CSP_FUNCTION
(R_OBJECT_ID, I_CHRONICLE_ID)
VALUES
('ID_102', 'Doc C');
INSERT INTO TEST_CSP_FUNCTION
(R_OBJECT_ID, I_CHRONICLE_ID)
VALUES
('ID_103', 'Doc D');
create or replace FUNCTION "FN_GET_TEST_CSP_TEMPLATE_DETAILS" (
TEMPLATE_USED_ID IN NVARCHAR2 DEFAULT NULL
)
RETURN VARCHAR2 AS
v_out VARCHAR2(100);
BEGIN
dbms_output.put_line(TEMPLATE_USED_ID);
SELECT I_CHRONICLE_ID INTO v_out FROM TEST_CSP_FUNCTION WHERE R_OBJECT_ID = TEMPLATE_USED_ID;
dbms_output.put_line(v_out);
return v_out;
end;
select FN_GET_TEST_CSP_TEMPLATE_DETAILS(N'ID_103') from dual;
returns:
Doc D
It works if row with appropriate ID exists in the table.
Demo:
SQL> select * From table_a;
R_OBJ I_CHRONICLE_ID
----- --------------
id_99 100
Function:
SQL> create or replace function fn_get_csp_template_details
2 (template_used_id in varchar2 default null)
3 return varchar2 as
4 v_out varchar2(100);
5 begin
6 dbms_output.put_line(template_used_id);
7 select i_chronicle_id
8 into v_out
9 from table_a
10 where r_object_id = template_used_id;
11 dbms_output.put_line(v_out);
12 return v_out;
13 end;
14 /
Function created.
Testing:
SQL> set serveroutput on
SQL> select fn_get_csp_template_details('id_99') as result from dual;
RESULT
--------------------------------------------------------------------------------
100
id_99
100
SQL>
If ID doesn't exist, it'll return NULL (if called from SELECT statement, as in my example):
SQL> select fn_get_csp_template_details('ABC') as result from dual;
RESULT
--------------------------------------------------------------------------------
SQL>
It'll return an exception if called elsewhere:
SQL> declare
2 l_result varchar2(100);
3 begin
4 l_result := fn_get_csp_template_Details('ABC');
5 end;
6 /
declare
*
ERROR at line 1:
ORA-01403: no data found
ORA-06512: at "SCOTT.FN_GET_CSP_TEMPLATE_DETAILS", line 6
ORA-06512: at line 4
SQL>
That's how it goes. So, what did you do to make it NOT work as expected?

I want to Create a function named VALIDATE_EMP which accepts employeeNumber as a parameter, Returns TRUE or FALSE depending on existence

Sample Data
create table Employees (emp_id number, emp_name varchar2(50), salary number, department_id number) ;
insert into Employees values(1,'ALex',10000,10);
insert into Employees values(2,'Duplex',20000,20);
insert into Employees values(3,'Charles',30000,30);
insert into Employees values(4,'Demon',40000,40);
Code :
create or replace function validate_emp(empno in number)
return boolean
is lv_count number
begin
select count(employee_id) into lv_count from hr.employees where employee_id = empno;
if lv_count >1 then
return true;
else
return false;
end;
I want to Create a function named VALIDATE_EMP which accepts empno as a parameter, Returns TRUE if the specified employee exists in the table name “Employeee” else FALSE.
missing semi-colon as terminator of the local variable declaration
if user you're connected to isn't hr, remove it (otherwise, leave it as is)
column name is emp_id, not employee_id
missing end if
When fixed, code compiles:
SQL> CREATE OR REPLACE FUNCTION validate_emp (empno IN NUMBER)
2 RETURN BOOLEAN
3 IS
4 lv_count NUMBER;
5 BEGIN
6 SELECT COUNT (emp_id)
7 INTO lv_count
8 FROM employees
9 WHERE emp_id = empno;
10
11 IF lv_count > 1
12 THEN
13 RETURN TRUE;
14 ELSE
15 RETURN FALSE;
16 END IF;
17 END;
18 /
Function created.
SQL>
How to call it? Via PL/SQL as Oracle's SQL doesn't have the Boolean datatype.
SQL> set serveroutput on
SQL> declare
2 result boolean;
3 begin
4 result := validate_emp(1);
5
6 dbms_output.put_line(case when result then 'employee exists'
7 else 'employee does not exist'
8 end);
9 end;
10 /
employee does not exist
PL/SQL procedure successfully completed.
SQL>
Maybe you'd rather return VARCHAR2; then you'd mimic Boolean, but you'd be able to use it in plain SQL:
SQL> CREATE OR REPLACE FUNCTION validate_emp (empno IN NUMBER)
2 RETURN VARCHAR2
3 IS
4 lv_count NUMBER;
5 BEGIN
6 SELECT COUNT (emp_id)
7 INTO lv_count
8 FROM employees
9 WHERE emp_id = empno;
10
11 IF lv_count > 1
12 THEN
13 RETURN 'TRUE';
14 ELSE
15 RETURN 'FALSE';
16 END IF;
17 END;
18 /
Function created.
SQL> select validate_emp(1) from dual;
VALIDATE_EMP(1)
-------------------------------------------------------------------
FALSE
SQL>

How to pass string of comma-separated numbers to stored procedure in condition for numeric field?

I have a stored procedure like below where multiple employee IDs will be passed as comma-separated value (multiple IDs). It is throwing error as "ORA-01722: invalid number". I know it's because of passing varchar2 variable for the numeric ID column. But is there any way we can achieve this simply?
create or replace PROCEDURE Fetch_Emp_Name(Emp_id in varchar2)
IS
BEGIN
select Name from EMP where id in (emp_id);
END;
You can use dynamic sql.
create or replace PROCEDURE Fetch_Emp_Name(emp_id in varchar2) IS
v_result varchar2;
begin
execute immediate
'select Name from EMP where id in (' || 'emp_id' || ')'
into
v_result;
end;
Also you can use package dbms_sql for dynamic sql.
Update
Another approach. I think may be better.
create or replace PROCEDURE Fetch_Emp_Name(emp_id in varchar2) IS
v_result varchar2;
begin
select
Name
from
EMP
where
id in
(
select
to_number(regexp_substr(emp_id, '[^,]+', 1, level))
from
dual
connect by regexp_substr(emp_id, '[^,]+', 1, level) is not null
);
exception
when no_data_found then
-- error1;
when too_many_rows then
-- error2;
end;
Sorry for before, I did not get the question in the right way. If you get a lot of IDs as different parameters, you could retrieve the list of names as an string split by comma as well. I put this code where I handled by regexp_substr the name of different emp_ids you might enter in the input parameter.
Example ( I am assuming that the IDs are split by comma )
create or replace PROCEDURE Fetch_Emp_Name(p_empid in varchar2) IS
v_result varchar2(4000);
v_append emp.name%type;
v_emp emp.emp_id%type;
counter pls_integer;
i pls_integer;
begin
-- loop over the ids
counter := REGEXP_COUNT(p_empid ,'[,]') ;
--dbms_output.put_line(p_empid);
if counter > 0
then
i := 0;
for r in ( SELECT to_number(regexp_substr(p_empid,'[^,]+',1,level)) as mycol FROM dual CONNECT BY LEVEL <= REGEXP_COUNT(p_empid ,'[,]')+1 )
loop
--dbms_output.put_line(r.mycol);
v_emp := r.mycol ;
select name into v_append from emp where emp_id = v_emp;
if i < 1
then
v_result := v_append ;
else
v_result := v_result ||','|| v_append ;
end if;
i := i + 1;
end loop;
else
v_emp := to_number(p_empid);
select name into v_result from emp where emp_id = v_emp;
end if;
dbms_output.put_line(v_result);
exception
when no_data_found
then
raise_application_error(-20001,'Not Employee found for '||v_emp||' ');
when too_many_rows
then
raise_application_error(-20002,'Too many employees for id '||v_emp||' ');
end;
Test
SQL> create table emp ( emp_id number, name varchar2(2) ) ;
Table created.
SQL> insert into emp values ( 1 , 'AA' );
1 row created.
SQL> insert into emp values ( 2 , 'BB' ) ;
1 row created.
SQL> commit;
SQL> insert into emp values ( 3 , 'CC' ) ;
1 row created.
SQL> select * from emp ;
EMP_ID NA
---------- --
1 AA
2 BB
3 CC
SQL> exec Fetch_Emp_Name('1') ;
AA
PL/SQL procedure successfully completed.
SQL> exec Fetch_Emp_Name('1,2,3') ;
AA,BB,CC
PL/SQL procedure successfully completed.
SQL>

Is there any way to assign a select query to local variable in PL/SQL other than select into statement?

Is there any way to assign a select query to local variable in PL/SQL other than select into statement?. Because select into throwing null value exception if the select query returns null value. Thanks
It would be helpful to post your code, but here is an example that should show the behavior you need. Assume there is a table called courses_tbl:
declare
cnumber number := NULL;
CURSOR c1
IS
SELECT course_number
FROM courses_tbl
WHERE course_name = 'XYZ';
BEGIN
open c1;
fetch c1 into cnumber;
if c1%notfound then
-- Do something here if you care about not found.
cnumber := 999; -- whatever
end if;
you can read about cursor attributes here
Seems that you need to use the exception handling as follows:
... -- Your procedure other code
BEGIN
SELECT <COLUMN_NAME> INTO <YOUR_VARIABLE>
FROM .....
WHERE ....
EXCEPTION WHEN NO DATA FOUND THEN
<YOUR_VARIABLE> := NULL;
END;
... -- Your procedure other code
You can use EXECUTE IMMEDIATE...INTO...:
DECLARE
nCnumber NUMBER;
BEGIN
EXECUTE IMMEDIATE 'SELECT CNUMBER FROM COURSES_TBL WHERE CNUMBER = 1'
INTO nCnumber;
DBMS_OUTPUT.PUT_LINE('SELECT #1 : nCnumber = ' || nCnumber);
nCnumber := NULL;
BEGIN
EXECUTE IMMEDIATE 'SELECT CNUMBER FROM COURSES_TBL WHERE CNUMBER = 100'
INTO nCnumber;
DBMS_OUTPUT.PUT_LINE('SELECT #2 : nCnumber = ' || nCnumber);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('SELECT #2 : NO DATA FOUND');
END;
END;
db<>fiddle here
You've seen how to do it using a cursor or exception handling section (which is - in my opinion - the right way to do it). However, as we're discussing, here's yet another option - an aggregate function. It won't return NO_DATA_FOUND but NULL.
This is what you have now:
SQL> declare
2 l_job emp.job%type;
3 begin
4 select job
5 into l_job
6 from emp
7 where ename = 'Does not exist';
8 end;
9 /
declare
*
ERROR at line 1:
ORA-01403: no data found
ORA-06512: at line 4
This is what you might do:
SQL> declare
2 l_job emp.job%type;
3 begin
4 select max(job)
5 into l_job
6 from emp
7 where ename = 'Does not exist';
8 end;
9 /
PL/SQL procedure successfully completed.
SQL>

passing parameter to a function in a select statement dynamically

how to use dynamic value while passing the parameter in select statement.
I have a function like below ....
create or replace function get_old_GSL (p_vendor_site_id in varchar2) return varchar2
is
v_sup_gsl VARCHAR2(30);
v_ret_val VARCHAR2(30);
BEGIN
BEGIN
SELECT SUBSTR(ATTRIBUTE52,1,6)
INTO v_sup_gsl
FROM gecm_dff_ext
WHERE primary_table ='AP_SUPPLIER_SITES_ALL'
AND primary_key = p_vendor_site_id
and attribute52 is not null;
EXCEPTION
WHEN OTHERS THEN
v_sup_gsl := NULL;
END;
BEGIN
IF v_sup_gsl IS NOT NULL THEN
SELECT segment1
INTO v_ret_val
FROM ap_suppliers
WHERE segment1 = v_sup_gsl;
END IF;
EXCEPTION
WHEN OTHERS THEN
v_ret_val := NULL;
END;
return(v_ret_val);
END;
Testing the function:
---------------------
--> select GET_OLD_GSL(22758460) as xyz from dual;
--> select GET_OLD_GSL(22758529) as xyz from dual;
--> select GET_OLD_GSL(22760317) as xyz from dual;
--> select GET_OLD_GSL(22758389) as xyz from dual;
--> select GET_OLD_GSL(22759519) as xyz from dual;
Here, iam using column 'vendor_site_id' of 'ap_supplier_sites_all' table .
My requirement is passing this parameter dynamically without hard coding the parameter each time i pass.

Resources