Errors when I create Oracle cursor - oracle

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;
/

Related

PL/SQL Oracle programming

I'm trying to enter data from table Towns(column place) to table F (column b).
and I would like to use (select * from Towns) in cursor but it is throwing an error.
Instead, if I use a (select place from towns) - my program works.
I want to incorporate (select * from Towns).
DECLARE
z_towns towns.place%TYPE;
CURSOR abc IS
(SELECT place FROM towns);
BEGIN
OPEN abc;
LOOP
FETCH abc INTO z_towns;
EXIT WHEN abc%NOTFOUND;
INSERT INTO F
(b)
VALUES(z_towns);
END LOOP;
CLOSE abc;
END;
You'll have to declare a different "target" variable - not just for the place column, but all columns in the towns table. Or, even better, a cursor variable which inherits type of the whole cursor return value (abc%rowtype):
DECLARE
CURSOR abc IS
SELECT * FROM towns;
abc_r abc%rowtype;
BEGIN
OPEN abc;
LOOP
FETCH abc INTO abc_r;
EXIT WHEN abc%NOTFOUND;
INSERT INTO F (b) VALUES(abc_r.place);
END LOOP;
CLOSE abc;
END;
That can be shortened if you use a cursor FOR loop as Oracle in that case does most of the job for you:
BEGIN
FOR abc IN (SELECT * FROM towns) LOOP
INSERT INTO F (b) VALUES(abc.place);
END LOOP;
END;
Certainly, the simplest (and fastest, because cursor's row-by-row is slow-by-slow and performance will suffer if there's a lot of data you're dealing with) option is to directly insert rows into the target table:
INSERT INTO F (b) SELECT place FROM towns;

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;

FORALL+ EXECUTE IMMEDIATE + INSERT Into tbl SELECT

I have got stuck in below and getting syntax error - Please help.
Basically I am using a collection to store few department ids and then would like to use these department ids as a filter condition while inserting data into emp table in FORALL statement.
Below is sample code:
while compiling this code i am getting error, my requirement is to use INSERT INTO table select * from table and cannot avoid it so please suggest.
create or replace Procedure abc(dblink VARCHAR2)
CURSOR dept_id is select dept_ids from dept;
TYPE nt_dept_detail IS TABLE OF VARCHAR2(25);
l_dept_array nt_dept_detail;
Begin
OPEN dept_id;
FETCH dept_id BULK COLLECT INTO l_dept_array;
IF l_dept_array.COUNT() > 0 THEN
FORALL i IN 1..l_dept_array.COUNT SAVE EXCEPTIONS
EXECUTE IMMEDIATE 'INSERT INTO stg_emp SELECT
Dept,''DEPT_10'' FROM dept_emp'||dblink||' WHERE
dept_id = '||l_dept_array(i)||'';
COMMIT;
END IF;
CLOSE dept_id;
end abc;
Why are you bothering to use cursors, arrays etc in the first place? Why can't you just do a simple insert as select?
Problems with your procedure as listed above:
You don't declare procedures like Procedure abc () - for a standalone procedure, you would do create or replace procedure abc as, or in a package: procedure abc is
You reference a variable called "dblink" that isn't declared anywhere.
You didn't put end abc; at the end of your procedure (I hope that was just a mis-c&p?)
You're effectively doing a simple insert as select, but you're way over-complicating it, plus you're making your code less performant.
You've not listed the column names that you're trying to insert into; if stg_emp has more than two columns or ends up having columns added, your code is going to fail.
Assuming your dblink name isn't known until runtime, then here's something that would do what you're after:
create Procedure abc (dblink in varchar2)
is
begin
execute immediate 'insert into stg_emp select dept, ''DEPT_10'' from dept_emp#'||dblink||
' where dept_id in (select dept_ids from dept)';
commit;
end abc;
/
If, however, you do know the dblink name, then you'd just get rid of the execute immediate and do:
create Procedure abc (dblink in varchar2)
is
begin
insert into stg_emp -- best to list the column names you're inserting into here
select dept, 'DEPT_10'
from dept_emp#dblink
where dept_id in (select dept_ids from dept);
commit;
end abc;
/
There appears te be a lot wrong with this code.
1) why the execute immediate? Is there any explicit requirement for that? No, than don't use it
2) where is the dblink variable declared?
3) as Boneist already stated, why not a simple subselect in the insert statement?
INSERT INTO stg_emp SELECT
Dept,'DEPT_10' FROM dept_emp#dblink WHERE
dept_id in (select dept_ids from dept );
For one, it would make the code actually readable ;)

Oracle 11g PL/SQL cursor,intersect query

I have the following query
select distinct name from table1
intersect
select distinct name from table2;
I load the resultset into a cursor in a PL/SQL procedure, like so:
cursor c1 is (select distinct name from table1
intersect
select distinct name from table2);
For some reason the last value in the resultset is duplicated in the cursor. This does not happen when running the query by itself. Any ideas why this is happening?
Code for the loop:
var table.col%type;
BEGIN
OPEN c1;
LOOP
BEGIN
exit when c1%NOTFOUND;
FETCH c1 into var;
INSERT INTO table values (col1, var);
commit;
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
CONTINUE;
END;
END LOOP;
END;
EXIT WHEN .. clause should come after FETCH.
Let's say, your cursor had 10 records to return. Before the first fetch is fired, %NOTFOUND evaluated to NULL, and the processing moves to the next statement, which is FETCH in your case. Now, if we fast forward to the 10th iteration, FETCH will get the 10th record, and the same is inserted into your destination table. The loop will move ahead, and since your EXIT WHEN %NOTFOUND is before fetch, it still has the value from last iteration, and it lets the control move ahead, and there, fetch will not be able to get any record, but the code will anyhow insert the last row it retrieved in 10th iteration. Now in the next loop, c1%NOTFOUND will be evaluated to TRUE and the loop will terminate
var table.col%type;
BEGIN
OPEN c1;
LOOP
BEGIN
FETCH c1 into var;
exit when c1%NOTFOUND;
INSERT INTO table values (col1, var);
commit;
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
CONTINUE;
END;
END LOOP;
END;
This is typical of the problems you see with explicit cursors.
Your first choice should be a single SQL statement, nothing more.
If you had to use a cursor, you should be using an implicit one wherever possible.

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

Resources