cannot perform dml operation inside a query in plsql - oracle

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

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?

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>

ORA-06553: PLS-801: internal error [55018] when testing function returning ROWTYPE

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>

converting a plsql code into a function

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;

how to dynamically insert column value into table2 select * from another table1 which is having one column less than table2

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;

Resources