PLSQL- UPDATE USING CURSOR CAUSING RECURSIVE LOOP - oracle

DECLARE
CURSOR emp_cur
IS
SELECT ename,empno,dname,sal
FROM emp3
WHERE dname='SALES';
BEGIN
FOR emp_rec IN emp_cur
LOOP
IF emp_rec.dname='SALES' THEN
UPDATE emp3
SET sal = sal + sal*0.10
WHERE empno = emp_rec.empno;
END IF;
END LOOP;
END;
*Cause: An attempt was made to go more than the specified number
of recursive SQL levels.
*Action: Remove the recursive SQL, possibly a recursive trigger.
I am getting this error while trying to update using cursor. What changes should I do?

The code u wrote doesnt make any sense to me, 1st why would u use pls sql cursor instead of simple update..set..where sql command, 2nd why loop 'for loop'
through cursor when u can simply write: for i in (select statement), 3rd this line: where empno = emp_rec.empno; is nonsensical, u are looping through cursor u are not joining tables.

Related

PL/SQL Cursor whose query is based on a variable

I've got a PL/SQL block that's basically
DECLARE
PIDM NUMBER(8);
CLM_TEST_SCORE NUMBER(5);
CURSOR C_STUDENT IS
select PIDM
from SOSC.DW_ALL_COLLECTOR
order by PIDM;
CURSOR C_CLM_SCORES IS
select max(to_number(SORTEST_TEST_SCORE))
from SATURN.SORTEST
where SORTEST_PIDM = pidm;
BEGIN
OPEN C_STUDENT;
LOOP
CLM_TEST_SCORE := '';
FETCH c_Student INTO pidm;
EXIT WHEN c_Student%notfound;
OPEN C_CLM_SCORES;
FETCH C_CLM_SCORES INTO CLM_TEST_SCORE;
CLOSE C_CLM_SCORES;
insert into some_table (CLM_TEST_SCORE)
values (CLM_TEST_SCORE);
END LOOP
END
As far as I'm aware, the pidm referred to in C_CLM_SCORES is the PIDM NUMBER(8) declared in line 2. That would mean that the query the cursor refers to mutates every iteration of the LOOP, depending on the current value of pidm. That doesn't jive with my understanding of cursors as a query-in-progress, as the underlying query changes every LOOP. Maybe it's the original author taking advantage of a clever DB algorithm?
This code works. I just have absolutely no idea why. What the heck is going on here?
You have an overly confusing block of code that is a nightmare to debug as you have:
SQL statements that refer to column name and local variables with the same identifier (PIDM and CLM_TEST_SCORE).
Cursors that change every iteration because they contain a bind variable referring to local variables (PIDM).
Highly inefficient use of loops.
If you want to make it clearer, you can rewrite the PL/SQL block so that you do not have duplicate identifiers and use a parameterised cursor:
DECLARE
v_PIDM SOSC.DW_ALL_COLLECTOR.PIDM%TYPE;
v_CLM_TEST_SCORE some_table.CLM_TEST_SCORE%TYPE;
CURSOR C_STUDENT IS
select PIDM
from SOSC.DW_ALL_COLLECTOR
order by PIDM;
CURSOR C_CLM_SCORES(p_pidm NUMBER) IS
select max(to_number(SORTEST_TEST_SCORE))
from SATURN.SORTEST
where SORTEST_PIDM = p_pidm;
BEGIN
OPEN C_STUDENT;
LOOP
FETCH c_Student INTO v_pidm;
EXIT WHEN c_Student%notfound;
OPEN C_CLM_SCORES(v_pidm);
FETCH C_CLM_SCORES INTO v_CLM_TEST_SCORE;
CLOSE C_CLM_SCORES;
insert into some_table (CLM_TEST_SCORE)
values (v_CLM_TEST_SCORE);
END LOOP;
END;
/
However, that is still very inefficient as each iteration performs a SELECT and an INSERT and will generate log entries. You can make it much simpler and more efficient to rewrite the whole thing as a single SQL statement:
INSERT INTO some_table (clm_test_score)
SELECT ( select max(to_number(SORTEST_TEST_SCORE))
from SATURN.SORTEST s
where s.SORTEST_PIDM = c.pidm )
FROM SOSC.DW_ALL_COLLECTOR c;
db<>fiddle here
The code in the question is an advertisement for "Why should implicit cursors be used?". If you rewrite your code as below it becomes much easier to understand:
BEGIN
FOR rowStudent IN (select PIDM
from SOSC.DW_ALL_COLLECTOR
order by PIDM)
LOOP
FOR rowScores IN (select max(to_number(SORTEST_TEST_SCORE)) AS CLM_TEST_SCORE
from SATURN.SORTEST
where SORTEST_PIDM = rowStudent.PIDM)
LOOP
insert into some_table (CLM_TEST_SCORE)
values (rowScores.CLM_TEST_SCORE);
END LOOP; -- rowScores
END LOOP; -- rowStudent
END;
This eliminates all of the variables and cursor definitions, and all the code is right in front of you where you can see it at a glance.
If you wanted to tighten it up a bit further you could use a join to get down to just one cursor:
BEGIN
FOR rowStudent_scores IN (SELECT d.PIDM, MAX(TO_NUMBER(s.SORTEST_TEST_SCORE)) AS CLM_TEST_SCORE
FROM SOSC.DW_ALL_COLLECTOR d
INNER JOIN SATURN.SORTEST s
ON s.SORTEST_PIDM = d.PIDM
GROUP BY d.PIDM)
LOOP
insert into some_table (CLM_TEST_SCORE)
values (rowStudent_scores.CLM_TEST_SCORE);
END LOOP; -- rowStudent_scores
END;

Errors when I create Oracle cursor

I am new to Oracle programming (started a month ago).
I've created a cursor to retrieve a value from a table 'CDF_LU' and then use the cursor to insert into another table 'test_1'. However there is an error when I run it.
Here is my code:
DECLARE
c_cdf_table CDF_LU.PROD_COLUMN_NAME%type;
-- create cursor.
CURSOR c_CDF_Table_Name IS
SELECT PROD_COLUMN_NAME
FROM CDF_LU
ORDER BY CDF;
-- create record.
c_cdf_table c_CDF_Table_Name%ROWTYPE;
BEGIN
OPEN c_CDF_Table_Name;
LOOP
FETCH c_CDF_Table_Name INTO c_cdf_table;
EXIT WHEN c_CDF_Table_Name%NOTFOUND;
-- insert to table_1.
INSERT into test_1
select A,B,C from table_1 where some_conditions
END LOOP;
CLOSE c_CDF_Table_Name;
END;
When I run this code, there are following errors:
In line 'FETCH c_CDF_Table_Name INTO c_cdf_table;', SQL statement ignored.
In line 'FETCH c_CDF_Table_Name INTO c_cdf_table;', at most one declaration for "C_CDF_TABLE" is permitted.
In line 'INSERT into test_1', SQL statement ignored.
I wrote the SQL codes above by strictly following the syntax of cursors, so I'm not sure where the problem is.
Could you please advise? Thank you!
The way you wanted to do it is possible (of course) when errors are fixed; something like this:
declare
-- cursor
cursor c_cdf_table_name is
select prod_column_name
from cdf_lu
order by cdf;
-- cursor variable
c_cdf_table c_cdf_table_name%rowtype;
begin
open c_cdf_table_name;
loop
fetch c_cdf_table_name into c_cdf_table;
exit when c_cdf_table_name%notfound;
insert into test1 (col1, col2, co3)
select a, b, c from table1
where d = c_cdf_table.prod_column_name;
end loop;
close c_cdf_table_name;
end;
/
However, there's a way shorter & simpler option - a cursor FOR loop. As you can see, you don't have to declare a cursor variable, open the cursor, fetch from it, take care about exiting the loop nor closing the cursor - Oracle does all that for you:
begin
for cur_r in (select prod_column_name
from cdf_lu
order by cdf)
loop
insert into test1 (col1, col2, co3)
select a, b, c from table1
where d = c_cdf_table.prod_column_name;
end loop;
end;
/

I am getting an error message in Oracle SQL stored procedure

I am trying the following code
CREATE OR REPLACE PROCEDURE sal2
AS
BEGIN
SELECT sal
FROM emp
END
And I'm getting this error
Error at line 7: PLS-00103: Encountered the symbol "end-of-file" 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
<< continue close current delete fetch lock
insert open rollback savepoint set sql execute commit forall
merge pipe purge
5. from emp
6. return 1
7. END
What I want to do is to get a sal column from emp table.
There are multiple issues with code as following;
create or replace procedure sal2
AS
Lv_var number; -- added variable for holding select value
BEGIN
select sal
Into lv_var -- into is needed in select
from emp; -- you missed this semicolon
-- you must use where clause as into variable can hold single value
-- so you must restrict this query to return only 1 record.
END; -- you missed this semicolon
/
Cheers!!
First you have to put the semicolon at the end of each line and after the END keyword. Then the SELECT statement is also wrong you have to load the result in a variable.
Here is the solution for multiple rows.
CREATE OR REPLACE PROCEDURE sal2
AS
CURSOR c_salaries IS SELECT salary FROM employees;
TYPE t_salaries IS TABLE OF c_salaries%rowtype INDEX BY BINARY_INTEGER;
v_salaries t_salaries;
BEGIN
OPEN c_salaries;
FETCH c_salaries BULK COLLECT INTO v_salaries;
CLOSE c_salaries;
END;
And one for a single row result.
CREATE OR REPLACE PROCEDURE sal2
AS
v_salary employees.salary%rowtype;
BEGIN
SELECT salary INTO v_salary
FROM employees WHERE employee_id = 100;
END;

Select Statement in oracle procedure

I can't able to create a select statement in oracle procedure. Please help me to create this.
Now I create the insert,update.delete statement in a procedure but i can't create a select statement. Please help me to create the select statement using cursor.
c_dbuser OUT SYS_REFCURSOR
ELSIF (TYPE_ =1) THEN
OPEN c_dbuser FOR
SELECT * FROM tbl_discount_master ;
CLOSE c_dbuser;
END IF;
call procedure_name(xx,xx,xx,1);
how can i get the selected value using call procedure statement.
In addition to the other suggestion, you have this solution when you are getting exactly one row.
DECLARE
myvar1 mytable.mycolumn1%TYPE;
myvar2 mytable.mycolumn2%TYPE;
BEGIN
SELECT mycolumn1, mycolumn2
INTO myvar1, myvar2
FROM mytable
WHERE …;
END;
This will throw an exception if there is no selected row (NO_DATA_FOUND) or if there is more than one row (TOO_MANY_ROWS).
The difference between select and the insert/update/delete is that you need to select into some structure, either one or more variables or a rowtype variable.
Avoid explicit cursors whenever possible in favour of the faster, less verbose and less error prone implicit cursor.
eg.
for cur_my_query in
select column1,
column2,
...
from ...
where ...
loop
refer here to cur_my_query or my_query.column1 etc
end loop

Reasonable SELECT ... INTO Oracle solution for case of multiple OR no rows

I just want to SELECT values into variables from inside a procedure.
SELECT blah1,blah2 INTO var1_,var2_
FROM ...
Sometimes a large complex query will have no rows sometimes it will have more than one -- both cases lead to exceptions. I would love to replace the exception behavior with implicit behavior similiar to:
No rows = no value change, Multiple rows = use last
I can constrain the result set easily enough for the "multiple rows" case but "no rows" is much more difficult for situations where you can't use an aggregate function in the SELECT.
Is there any special workarounds or suggestions? Looking to avoid significantly rewriting queries or executing twice to get a rowcount before executing SELECT INTO.
Whats wrong with using an exception block?
create or replace
procedure p(v_job VARCHAR2) IS
v_ename VARCHAR2(255);
begin
select ename into v_ename
from (
select ename
from scott.emp
where job = v_job
order by v_ename desc )
where rownum = 1;
DBMS_OUTPUT.PUT_LINE('Found Rows Logic Here -> Found ' || v_ename);
EXCEPTION WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('No Rows found logic here');
end;
SQL> begin
p('FOO');
p('CLERK');
end; 2 3 4
5 /
No Rows found logic here
Found Rows Logic Here -> Found SMITH
PL/SQL procedure successfully completed.
SQL>
You could use a for loop. A for loop would do nothing for no rows returned and would be applied to every row returned if there where multiples. You could adjust your select so that it only returns the last row.
begin
for ARow in (select *
from tableA ta
Where ta.value = ???) loop
-- do something to ARow
end loop;
end;

Resources