Can cursor be use in UPDATE and DELETE in Function - oracle

I have one doupt while I try to use UPDATE and DELETE method.
So starting from basic function something like
FUNCTION GET_ANSWER(p_questionId IN INT)
RETURN SYS_REFCURSOR IS
rc SYS_REFCURSOR;
/*getAnswer*/
BEGIN
OPEN rc FOR
SELECT * FROM answers WHERE AnswerID = p_questionId;
RETURN rc;
END GET_ANSWER;
While I try to use DELETE or UPDATE method I get error message
Error(3529,1): PLS-00103: Encountered the symbol "DELETE" when expecting one of the following: ( - + case mod new not null select with <an identifier> <a double-quoted delimited-identifier> <a bind variable> continue avg count current exists max min prior sql stddev sum variance execute forall merge time timestamp interval date <a string literal with character set specification> <a number> <a single-quoted SQL string> pipe <an alternatively-quoted string literal with character set specification>
Error(3530,1): PLS-00103: Encountered the symbol "RETURN"
Error(3536,1): PLS-00103: Encountered the symbol "END"
FUNCTION DELETE_ACTIVITY(p_activityId IN INT)
RETURN SYS_REFCURSOR IS
rc SYS_REFCURSOR;
BEGIN
OPEN rc FOR
DELETE FROM activities WHERE id = p_activityId;
RETURN rc;
END DELETE_ACTIVITY;
What is wrong here ? Wher did I made mistake ?

Don't do it via cursor, but simply
DELETE FROM answers WHERE AnswerID = p_questionId;
Also, make it a procedure, not a function.
As of UPDATE, well - it depends on what you want to update and how, but - generally:
update answers set
some_column = some_value
where ansewrID = p_questionId
Why a procedure and not a function:
SQL> create or replace function f_test return number is
2 begin
3 delete from test;
4 return 1;
5 end;
6 /
Function created.
SQL> select f_test from dual;
select f_test from dual
*
ERROR at line 1:
ORA-14551: cannot perform a DML operation inside a query
ORA-06512: at "SCOTT.F_TEST", line 3
SQL>
However, if a function is an autonomous transaction, you can do that, but - not recommended:
SQL> create or replace function f_test return number is
2 pragma autonomous_transaction;
3 begin
4 delete from test;
5 commit;
6 return 1;
7 end;
8 /
Function created.
SQL> select f_test from dual;
F_TEST
----------
1
SQL>

Cursors are only used for SELECT statements. You cannot open a cursor for an INSERT, UPDATE or DELETE statement.
See the documentation
An explicit cursor definition has this syntax:
CURSOR cursor_name [ parameter_list ] [ RETURN return_type ] IS
select_statement;

Related

How to define a PL/SQL function and call it from another language

I am trying to write a PL/SQL function that returns a number. I must use the number in SQL where clause. The function is in the following:
CREATE OR REPLACE FUNCTION func_id(str_id IN STRING,num_group IN NUMBER)
RETURN NUMBER
IS
result NUMBER;
declare
temp STRING;
BEGIN
temp := substr(str_id, -least(length(str_id), 2));
result := TO_NUMBER(temp) % num_group;
RETURN result;
END;
select * from table where func_id("id",2)=1
2 and 1 are just an example. I want to call the function in my Scala Program that variables are replaced in place of 2 and 1.
When I run the code in SQL Developer I receive this error:
Function FUNC_ID compiled
LINE/COL ERROR
--------- -------------------------------------------------------------
5/1 PLS-00103: Encountered the symbol "DECLARE" when expecting one of the following: begin function pragma procedure subtype type <an identifier> <a double-quoted delimited-identifier> current cursor delete exists prior The symbol "begin" was substituted for "DECLARE" to continue.
13/54 PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following: . , # ; for <an identifier> <a double-quoted delimited-identifier> group having intersect minus order partition start subpartition union where connect sample
Would you please guide me how to write a PL/SQL function and call it in another query or any where else?
Any help is really appreciated.
DECLARE is syntactically invalid in that position;
STRING is not a valid data type, you want VARCHAR2;
% is not a valid operator, you want the MOD function; and
The intermediate variables are not required.
CREATE OR REPLACE FUNCTION func_id(
str_id IN VARCHAR2,
num_group IN NUMBER
) RETURN NUMBER
IS
BEGIN
RETURN MOD(TO_NUMBER(substr(str_id, -LEAST(2, LENGTH(str_id)))), num_group);
END;
/
Then, if you have the table:
CREATE TABLE table_name ( id ) AS
SELECT '1' FROM DUAL UNION ALL
SELECT '24' FROM DUAL UNION ALL
SELECT '10-05' FROM DUAL;
You can call the function using:
select id,
func_id(id, 23)
from table_name
Which outputs:
ID
FUNC_ID(ID,23)
1
1
24
1
10-05
5
db<>fiddle here

Function created with compilation error in PLSQL

When I compile the below code I am getting a error message "Function created with compilation errors"
create or replace function find_port(ip_ID in int) RETURN int
is
t_count number;
count varchar;
begin
select is_rail into count from table where id = ip_ID;
case
when count ='True' then t_count:=1;
when count ='False' then t_count:=0;
end case;
end;
/
i am getting a error message "Function created with compilation errors"
So the question you should be asking is, "how do I get a list of compilation errors for my PL/SQL code?"
Other people have told you how to fix the current errors in your code, but the more important skill is that you find out how to diagnose your code for yourself.
Oracle is a database, and it stores metadata in a set of special views called the data dictionary. These views include views for compilation errors. This query will work in any SQL environments:
select name, type, line, text -- or just *, obvs
from user_errors ue
order by ue.name, ue.type, ue.sequence;
There are also ALL_ERRORS and DBA_ERRORS views. Find out more.
In SQL*Plus you can run sho err (short for show errors). IDEs like PL/SQL Developer or Oracle SQL Developer will show compilation errors automatically.
Once you know how to get the text of the errors you need to know that LINE will tell you the line where the error is raised. Although with certain classes of error (such as missing commas or unmatched brackets) the indicated line may not be the line where the actual error resides. Unfortunately there is still a need for interpretation and understanding, which requires experience.
Actually, COUNT can be used as a PL/SQL variable:
SQL> create or replace function f_test return int is
2 count number;
3 begin
4 select 1 into count from dual;
5 return 2;
6 end;
7 /
Function created.
SQL> select f_test from dual;
F_TEST
----------
2
SQL>
However, you can't return it:
SQL> create or replace function f_test return int is
2 count number;
3 begin
4 select 1 into count from dual;
5 return count;
6 end;
7 /
Warning: Function created with compilation errors.
SQL> show err
Errors for FUNCTION F_TEST:
LINE/COL ERROR
-------- -----------------------------------------------------------------
5/3 PL/SQL: Statement ignored
5/10 PLS-00204: function or pseudo-column 'COUNT' may be used inside a
SQL statement only
SQL>
Here, #priya, you can see how to help yourself - SHOW ERR will tell you what's wrong with your code.
Apart from that, CASE statement you used was invalidly written; should have been similar to this:
SQL> create or replace function f_test return int is
2 l_count number;
3 t_count number;
4 begin
5 select 1 into l_count from dual;
6
7 t_count := case when l_count = 1 then 1
8 when l_count = 2 then 2
9 end;
10
11 return t_count;
12 end;
13 /
Function created.
SQL> select f_test from dual;
F_TEST
----------
1
SQL>
count is a SQL function and thus not a better choice to be used as a PL/SQL variable. The CASE block can be used within the select statement.
Furthermore, your function does not RETURN any value.
create or replace function find_port(ip_ID in int) RETURN int
is
t_count number;
begin
select case
when is_rail = 'True' then 1
when is_rail = 'False' then 0
end into t_count from yourtable where id=ip_ID;
RETURN t_count;
end;

ORA-04091 mutating table error when calling a function from a procedure

I have a task:
Write a procedure to update salary (salary * % of increment) in emp table based on grade. Use function to get increment
This is my procedure:
CREATE OR REPLACE
PROCEDURE sal_incre
IS
CURSOR c_cur
IS
SELECT * FROM emp_task;
BEGIN
UPDATE emp_task SET sal = sal + sal_incr(grade_id);
FOR rec IN c_cur
LOOP
dbms_output.put_line(rec.empno||','||rec.ename||','||rec.sal);
END LOOP;
END;
This is my function code:
CREATE OR REPLACE
FUNCTION sal_incr(
p_grade NUMBER)
RETURN
IS
v_inc NUMBER;
BEGIN
SELECT raise_percent
INTO v_inc
FROM sal_inc
WHERE grade_id IN
(SELECT grade_id FROM emp_task WHERE grade_id = p_grade
);
RETURN v_inc;
COMMIT;
END;
When I call the procedure I'm getting:
ORA-04091: table SCOTT.EMP_TASK is mutating, trigger/function may not see it
ORA-06512: at "SCOTT.SAL_INCR", line 8
ORA-06512: at "SCOTT.SAL_INCRE", line 6
ORA-06512: at line 2
What am I doing wrong?
Your function is referring to the same table you're using in the procedure at the point you call that function, which is what causes this error. You're updating and querying it at the same time, in a way that could cause indeterminate (or confusing) results, even though you aren't querying the column you're updating. Oracle is protecting you from yourself here.
In your function you're doing:
SELECT raise_percent
INTO v_inc
FROM sal_inc
WHERE grade_id IN
(SELECT grade_id FROM emp_task WHERE grade_id = p_grade
);
There is no need to look at the emp_task table here. Unless you've been passed an nonexistent value (which can't happen from your procedure) the subquery can only return the original p_grade argument value, so this is the same as:
SELECT raise_percent
INTO v_inc
FROM sal_inc
WHERE grade_id = p_grade;
If you do that the function no longer refers to emp_task, so it won't throw the mutating trigger/function error when it's called as part of an update.
And your function should not be issuing a COMMIT - let the calling procedure, or preferably the session that calls the procedure, decide whether the who transaction should be committed or rolled back.
Also, from the title and column name it looks like raise_percent is a percentage, so you need to use that to find the value to multiply by - you shouldn't add that percentage figure. If that gives you a value of 2 for a 2% raise, for example, you need to either do this in your procedure:
UPDATE emp_task SET sal = sal * (1 + (sal_incr(grade_id)/100));
Or more neatly have your function return 1 + (raise_percent/100) and do:
UPDATE emp_task SET sal = sal * sal_incr(grade_id);
Change the procedure like this:
create or replace
procedure sal_incre
is
cursor c_cur is
select distinct e.grade_id,e.ename,e.sal from sal_inc s
join emp_task e on e.grade_id = s.grade_id order by e.ename ;
v_incr number;
begin
for f_cur in c_cur
loop
v_incr := sal_incr(f_cur.grade_id);
update emp_task set sal = sal + v_incr;
dbms_output.put_line('Emp Name : '||f_cur.ename||','
||' Sal:'||f_cur.sal||','||' Grade: '||f_cur.grade_id);
end loop;
end;

Further PL/SQL issues [duplicate]

I am trying to create a page process in Oracle APEX 4.1. Specifically, When a button on this page is process submitting the page, I want this PL/SQL query to work. I don't have much of an understanding of PL/SQL and am looking to find out how to figure this issue out.
What I want the query to do:
I would like this page process to loop through each row in the EMPLOYEE table that I have in a database for APEX. For each row, I want to move the username, group and password into their own variables, and then to create an APEX user using the APEX_UTIL_CREATE_USER process. I want this to be done with every employee in the table.
I don't know specifically what is wrong with this PL/SQL, as I have never had to use it before. I would greatly appreciate any help anyone can give me with this. I will show the query and the error message below.
PL/SQL query:
PROCEDURE deploy_employee
(EMP_USERNAME IN EMPLOYEE)
IS
BEGIN
FOR indx IN NVL (EMP_USERNAME.FIRST, 0)
.. NVL (EMP_USERNAME.LAST, -1)
LOOP
emp_user EMPLOYEE.EMP_USERNAME%TYPE;
emp_pass EMPLOYEE.EMP_PASSWORD%TYPE;
emp_group EMPLOYEE.EMP_GROUP%TYPE;
BEGIN
BEGIN
select EMP_USERNAME into emp_user from EMPLOYEE;
select EMP_PASSWORD into emp_pass from EMPLOYEE;
select EMP_GROUP into emp_group FROM EMPLOYEE;
EXCEPTION
WHEN NO_DATA_FOUND THEN
emp_user := NULL;
emp_pass := NULL;
emp_group := NUL;
END;
APEX_UTIL.CREATE_USER(
p_user_name => emp_user,
p_web_password => emp_pass,
p_user_group => emp_gorup,
);
END;
END LOOP;
END deploy_employee;
Error message:
1 error has occurred ORA-06550: line 2, column 1: PLS-00103:
Encountered the symbol "PROCEDURE" when expecting one of the
following: ( begin case declare end exception exit for goto if loop
mod null pragma raise return select update while with <an identifier>
<a double-quoted delimited-identifier> <a bind variable> << continue
close current delete fetch lock insert open rollback savepoint set sql
execute commit forall merge pipe purge The symbol "declare" was
substituted for "PROCEDURE" to continue. ORA-065
The page number is 2.
Once again I would be greatly appreciative of any help I could gain.
There are multiple issues with your procedure.
You are missing the CREATE keyword, and that's the root cause for the compile time error. PLS-00103.
See the documentation for more details on CREATE PROCEDURE statement to create a standalone stored procedure or a call specification.
EMP_USERNAME IN EMPLOYEE
The data type declaration for the IN parameter is incorrect. You need to do it as:
EMP_USERNAME IN EMPLOYEE.EMP_USERNAME%TYPE
The FOR LOOP is syntactically incorrect.
FOR indx IN NVL (EMP_USERNAME.FIRST, 0) .. NVL (EMP_USERNAME.LAST, -1)
You could do it as:
SQL> CREATE OR REPLACE
2 PROCEDURE deploy_emp(
3 i_emp emp.empno%type)
4 IS
5 emp_user VARCHAR2(50);
6 BEGIN
7 FOR indx IN
8 (SELECT ename FROM emp
9 )
10 LOOP
11 BEGIN
12 BEGIN
13 SELECT ename INTO emp_user FROM emp WHERE empno = i_emp;
14 EXCEPTION
15 WHEN NO_DATA_FOUND THEN
16 emp_user := NULL;
17 END;
18 END;
19 END LOOP;
20 dbms_output.put_line(emp_user);
21 END deploy_emp;
22 /
Procedure created.
SQL> sho err
No errors.
Now, let's test it and see:
SQL> set serveroutput on
SQL> EXEC deploy_emp(7369);
SMITH
PL/SQL procedure successfully completed.
SQL>
use CREATE PROCEDURE to create a new stored procedure or EXEC <proc name> to execute it
If you whant do all this in a page process, you don't need to create procedure. Put this code in the source of your page process:
BEGIN
FOR indx IN (
select *
from EMPLOYEE
) LOOP
APEX_UTIL.CREATE_USER(
p_user_name => indx.EMP_USERNAME,
p_web_password => indx.EMP_PASSWORD,
p_user_group => indx.EMP_GROUP,
);
END LOOP;
END;

What Causes the Numeric Overflow in this PL/SQL Function?

I can run this command on Oracle 10.2 without problem:
SQL> select instr(unistr('foo'), chr(4050596145)) as hazit from dual;
HAZIT
----------
0
So I tried to encapsulate it into a function:
CREATE OR REPLACE FUNCTION hazit(string IN VARCHAR2) RETURN INTEGER
AS
BEGIN
RETURN instr(unistr(string), chr(4050596145));
END;
/
Function created.
But I get a numeric overflow error when I try to use it:
SQL> select hazit('foo') FROM DUAL;
select hazit('foo') FROM DUAL
*
ERROR at line 1:
ORA-01426: numeric overflow
ORA-06512: at "DWHEELER.HAZIT", line 4
What gives?
I don't have an explanation but this seems to work:
CREATE OR REPLACE FUNCTION hazit(string IN VARCHAR2) RETURN NUMBER IS
i number;
BEGIN
select instr(unistr(string), chr(4050596145))
into i from dual;
return i;
END;
/
Here is a fiddle

Resources