Here in this below function i want to insert values into table ba_acct_cust_lob_xref according to account number and customer id. And account number im fetching from ch_acct_mast table and customer id from cl_custmast table. How i can achieve this using plsql function. when i tried it is showing error like cannot perform dml opertaion inside a query.
CREATE OR REPLACE FUNCTION AP_EXT_MNT_BAM62
(
var_typ_entity CHAR,
var_cod_acct_no CHAR,
var_cod_cust_id NUMBER,
var_cod_ao_business varchar2,
var_cod_ao_operations varchar2,
var_cod_lob NUMBER
)
RETURN NUMBER
AS
BEGIN
IF var_typ_entity = 'A' THEN
BEGIN
insert into ba_cust_acct_ao_lob_xref
(typ_entity,
Cod_cust_id,
cod_acct_no,
Cod_ao_business,
cod_ao_operations,
cod_lob,
flg_mnt_status,
cod_mnt_action,
cod_last_mnt_makerid,
cod_last_mnt_chkrid,
dat_last_mnt,
ctr_updat_srlno,
COD_ENTITY_VPD)
(select var_typ_entity,
var_cod_cust_id,
var_cod_acct_no,
var_cod_ao_business,
var_cod_ao_operations,
var_cod_lob,
flg_mnt_status,
cod_mnt_action,
cod_last_mnt_makerid,
cod_last_mnt_chkrid,
sysdate,
ctr_updat_srlno,
COD_ENTITY_VPD
from CH_ACCT_MAST
where cod_acct_no=var_cod_acct_no );
EXCEPTION
when no_data_found then
NULL;
END;
END IF;
IF var_typ_entity = 'C' THEN
BEGIN
insert into ba_cust_acct_ao_lob_xref
(typ_entity,
Cod_cust_id,
cod_acct_no,
Cod_ao_business,
cod_ao_operations,
cod_lob,
flg_mnt_status,
cod_mnt_action,
cod_last_mnt_makerid,
cod_last_mnt_chkrid,
dat_last_mnt,
ctr_updat_srlno,
COD_ENTITY_VPD)
(select var_typ_entity,
var_cod_cust_id,
var_cod_acct_no,
var_cod_ao_business,
var_cod_ao_operations,
var_cod_lob,
flg_mnt_status,
cod_mnt_action,
cod_last_mnt_makerid,
cod_last_mnt_chkrid,
sysdate,
ctr_updat_srlno,
COD_ENTITY_VPD
from CI_CUSTMAST
where Cod_cust_id=var_Cod_cust_id );
EXCEPTION
when no_data_found then
NULL;
END;
END IF;
return 1;
END;
Well, you can perform DML in a function and use it in a query, if it were an autonomous transaction. Don't do that, not here.
Here's an example, using this sample table:
SQL> SELECT * FROM test;
ID NAME
---------- --------------------
2 Little
3 Foot
Function is supposed to insert a row into that table (basically, that's what you have):
SQL> CREATE OR REPLACE FUNCTION f_test (par_id IN NUMBER, par_name IN VARCHAR2)
2 RETURN NUMBER
3 IS
4 BEGIN
5 INSERT INTO test (id, name)
6 VALUES (par_id, par_name);
7
8 RETURN 1;
9 END;
10 /
Function created.
Error you got is due to such a code:
SQL> SELECT f_test (5, 'Karthiga') FROM DUAL;
SELECT f_test (5, 'Karthiga') FROM DUAL
*
ERROR at line 1:
ORA-14551: cannot perform a DML operation inside a query
ORA-06512: at "SCOTT.F_TEST", line 5
If it were a PL/SQL procedure, then your function would work:
SQL> DECLARE
2 l_test NUMBER;
3 BEGIN
4 l_test := f_test (5, 'Karthiga');
5 END;
6 /
PL/SQL procedure successfully completed.
Or, as I previously said, if function was an autonomous transaction, you could use it in a query:
SQL> CREATE OR REPLACE FUNCTION f_test (par_id IN NUMBER, par_name IN VARCHAR2)
2 RETURN NUMBER
3 IS
4 PRAGMA AUTONOMOUS_TRANSACTION;
5 BEGIN
6 INSERT INTO test (id, name)
7 VALUES (par_id, par_name);
8
9 COMMIT;
10 RETURN 1;
11 END;
12 /
Function created.
SQL> SELECT f_test (7, 'Twitter') FROM DUAL;
F_TEST(7,'TWITTER')
-------------------
1
SQL>
See? Now it works, but that's not what you should be doing, really.
Result:
SQL> select * from test;
ID NAME
---------- --------------------
2 Little
3 Foot
5 Karthiga --> newly added
7 Twitter --> rows
SQL>
Why do you insist on a function? This is obviously a procedure code. Yes, you're returning 1 just because function has to return something, but there's no evidence that you do need a function. Functions calculate stuff and return the value. You're just inserting a row; that's what procedures do.
So:
CREATE OR REPLACE PROCEDURE AP_EXT_MNT_BAM62 (
var_typ_entity CHAR,
var_cod_acct_no CHAR,
var_cod_cust_id NUMBER,
var_cod_ao_business VARCHAR2,
var_cod_ao_operations VARCHAR2,
var_cod_lob NUMBER)
AS
BEGIN
IF var_typ_entity = 'A'
THEN
BEGIN
INSERT INTO ba_cust_acct_ao_lob_xref (typ_entity,
Cod_cust_id,
cod_acct_no,
Cod_ao_business,
cod_ao_operations,
cod_lob,
flg_mnt_status,
cod_mnt_action,
cod_last_mnt_makerid,
cod_last_mnt_chkrid,
dat_last_mnt,
ctr_updat_srlno,
COD_ENTITY_VPD)
(SELECT var_typ_entity,
var_cod_cust_id,
var_cod_acct_no,
var_cod_ao_business,
var_cod_ao_operations,
var_cod_lob,
flg_mnt_status,
cod_mnt_action,
cod_last_mnt_makerid,
cod_last_mnt_chkrid,
SYSDATE,
ctr_updat_srlno,
COD_ENTITY_VPD
FROM CH_ACCT_MAST
WHERE cod_acct_no = var_cod_acct_no);
EXCEPTION
WHEN NO_DATA_FOUND
THEN
NULL;
END;
END IF;
IF var_typ_entity = 'C'
THEN
BEGIN
INSERT INTO ba_cust_acct_ao_lob_xref (typ_entity,
Cod_cust_id,
cod_acct_no,
Cod_ao_business,
cod_ao_operations,
cod_lob,
flg_mnt_status,
cod_mnt_action,
cod_last_mnt_makerid,
cod_last_mnt_chkrid,
dat_last_mnt,
ctr_updat_srlno,
COD_ENTITY_VPD)
(SELECT var_typ_entity,
var_cod_cust_id,
var_cod_acct_no,
var_cod_ao_business,
var_cod_ao_operations,
var_cod_lob,
flg_mnt_status,
cod_mnt_action,
cod_last_mnt_makerid,
cod_last_mnt_chkrid,
SYSDATE,
ctr_updat_srlno,
COD_ENTITY_VPD
FROM CI_CUSTMAST
WHERE Cod_cust_id = var_Cod_cust_id);
EXCEPTION
WHEN NO_DATA_FOUND
THEN
NULL;
END;
END IF;
END;
If it must return 1, then make it an OUT parameter.
Related
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 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>
I need to test some function returning ROWTYPE variable in Toad. When I try to run it, I aget the Internal error.
I run is as
SELECT MYPACKAGE.MyFunction(param1, aram2, param3) FROM DUAL
Is there any way to test a function returning ROWTYPE for Toad?
As you just want to test the function you could use an anonymous PL/SQL block to call it and assign its result to a matching rowtype variable, e.g.:
declare
l_row mytable%rowtype;
begin
-- call the function and assign the result to a variable
l_row := mypackage.myfunction(1, 2, 3);
-- do something with the result
dbms_output.put_line(l_row.some_columns);
end;
/
Quick demo with a made-up table and expanded function:
create table mytable (col1, col2, col3, col4, col5) as
select 1, 2, 3, 'test', sysdate from dual;
create or replace package mypackage as
function myfunction (param1 number, param2 number, param3 number)
return mytable%rowtype;
end mypackage;
/
create or replace package body mypackage as
function myfunction (param1 number, param2 number, param3 number)
return mytable%rowtype is
l_row mytable%rowtype;
begin
select * into l_row
from mytable
where col1 = param1
and col2 = param2
and col3 = param3;
return l_row;
end myfunction;
end mypackage;
/
Calling from SQL gets the same error you see now:
select mypackage.myfunction(1, 2, 3) from dual;
SQL Error: ORA-06553: PLS-801: internal error [55018]
But with a block (run here through SQL Developer with output enabled):
set serveroutput on
declare
l_row mytable%rowtype;
begin
-- call the function and assign the result to a variable
l_row := mypackage.myfunction(1, 2, 3);
-- do something with the result
dbms_output.put_line(l_row.col4 ||':'|| l_row.col5);
end;
/
test:2019-04-29
PL/SQL procedure successfully completed.
db<>fiddle
True, it won't work. Function, when used in a SQL query, is supposed to return a SQL datatype, while %ROWTYPE is a PL/SQL record.
This is what you, probably, have now:
SQL> create or replace function f_test (par_deptno in number)
2 return dept%rowtype
3 is
4 retval dept%rowtype;
5 begin
6 select deptno, dname, loc
7 into retval
8 from dept
9 where deptno = par_deptno;
10 return retval;
11 end;
12 /
Function created.
SQL> select f_test(10) From dual;
select f_test(10) From dual
*
ERROR at line 1:
ORA-06553: PLS-801: internal error [55018]
SQL>
Option you might choose is to create (and return) an object type. Here's an example:
SQL> create or replace type dept_type as object
2 (deptno number,
3 dname varchar2(20),
4 loc varchar2(20));
5 /
Type created.
SQL> create or replace function f_test (par_deptno in number)
2 return dept_type
3 is
4 retval dept_type;
5 begin
6 select dept_type(deptno, dname, loc)
7 into retval
8 from dept
9 where deptno = par_deptno;
10 return retval;
11 end;
12 /
Function created.
SQL> select f_test(10).dname From dual;
F_TEST(10).DNAME
--------------------
ACCOUNTING
SQL>
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;
table1 :
t1_column1 t1_column2 t1_column3
1 2 3
2 3 4
table2 is :
t2_column1 t2_column2 t2_column3 t2_column4
1 2 3 0
2 3 4 0
Here I am passing the value as function argument, but when I am
trying to create this function getting this error : Error(11,7):
PL/SQL: SQL Statement ignored and Error(11,108): PL/SQL: ORA-00917:
missing comma
CREATE OR REPLACE FUNCTION NEW_PURCHASE(GODOWN_CODE IN VARCHAR2,MON IN
VARCHAR2)
RETURN VARCHAR2 IS
var_mon VARCHAR2(100);
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
var_mon:=MON;
INSERT INTO table2
( t2_column1, t2_column2, t2_column3, var_mon)
SELECT t1_column1, t1_column2, t1_column3
FROM table1 WHERE t1_column1=GODOWN_CODE;
DELETE FROM table1 WHERE t1_column1=GODOWN_CODE;
COMMIT;
RETURN 'done';
END NEW_PURCHASE;
Try this:
CREATE OR REPLACE FUNCTION NEW_PURCHASE (GODOWN_CODE IN VARCHAR2,
MON IN VARCHAR2)
RETURN VARCHAR2
IS
--var_mon VARCHAR2 (100);
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
--var_mon := 'MON';
INSERT INTO table2 (t2_column1,
t2_column2,
t2_column3,
t2_column4)
SELECT t1_column1, t1_column2, t1_column3, mon
FROM table1
WHERE t1_column1 = GODOWN_CODE;
DELETE FROM table1
WHERE t1_column1 = GODOWN_CODE;
COMMIT;
RETURN 'done';
END NEW_PURCHASE;
Note that if you do DML operation in a function, you cannot call it from a select statement, You would need a PLSQL block to run it.
For ex:
You can call it as below:
declare
l_message varchar2(30);
begin
l_message := test_func('123');
end;
... but not like this:
select test_func(empno) from emp;