ORA-06511: PL/SQL: cursor already open. I am closing my cursor but no luck - oracle

I am getting the ORA-06511: PL/SQL: cursor already open ERROR.
Not sure why I am getting this error since I am closing my cursor.
Please see code below.
BEGIN
OPEN findem; ---OPENING HERE!
FOR crfindem IN findem LOOP
FETCH findem into other1, other2, other3;
l_CollectionOfRows(Counter).tmps_key := other1;
l_CollectionOfRows(Counter).tmps_cfb_rate := other2;
l_CollectionOfRows(Counter).tmps_engagement_pay_rate := other3;
Counter := Counter + 1;
END LOOP;
CLOSE findem;---CLOSING HERE!
FORALL i IN l_CollectionOfRows.FIRST .. l_CollectionOfRows.LAST
UPDATE Base.Table
SET MARGIN = :PAGE56_MARGIN,
PERCENT = :PAGE56_MARGIN + l_CollectionOfRows(i).rate,
PAY_RATE = (l_CollectionOfRows(i).pay_rate * (:PAGE56_MARGIN + l_CollectionOfRows(i).rate)) + l_CollectionOfRows(i).pay_rate
WHERE tmps_key = l_CollectionOfRows(i).tmps_key;
END;
I read from some online threads that for every Insert/Update statement, Oracle will create an implicit cursor. If this is the case how do you treat those implicit cursors that Oracle creates?

You are getting that error because you are opening the same cursor twice: the FOR construct already does all these things for you:
FOR opens the cursor
FOR implicitly declares a record variable (the one named crfindem, in your code) that will receive the values for each row read from the cursor
FOR loops on every row and assigns the values of the current row to the crfindem variable
FOR automatically closes the cursor at the end of the loop
so you don't need any OPEN/CLOSE/FETCH .. INTO commands if you are using a FOR loop:
see this simple example: itjust works.
declare
cursor cur is select * from user_tab_comments;
begin
for c in cur loop
DBMS_OUTPUT.PUT_LINE( c.table_name || ' - ' || c.comments);
end loop;
end;
but if i try to open the cursor before using the for loop, I will get your same error because the cursor is already open and the for construct is trying to open it again:
declare
cursor cur is select * from user_tab_comments;
begin
open cur; -- this is not needed and will cause problems
for c in cur loop --! ERROR: here I am trying to open AGAIN the same cursor
DBMS_OUTPUT.PUT_LINE( c.table_name || ' - ' || c.comments);
end loop;
end;
so, you either must choose if you want to write this code:
declare
cursor cur is select * from user_tab_comments;
begin
for c in cur loop
DBMS_OUTPUT.PUT_LINE( c.table_name || ' - ' || c.comments);
end loop;
end;
or avoid using the FOR construct and do all the open/fetch/close operations by yourself, by writing this:
declare
-- I cant' use "select *" here:
-- if I use "fetch into" to a precise list of variables,
-- I have to extract exactly the fields I want to assign:
cursor cur is
select table_name,comments
from user_tab_comments;
tabname varchar(100);
tabcomment varchar2(4000);
begin
open cur;
loop
fetch cur into tabname,tabcomment;
exit when cur%notfound;
DBMS_OUTPUT.PUT_LINE( tabname || ' - ' || tabcomment);
end loop;
close cur;
end;
Your error is that your code is trying to do both these things at the same time.
you should have written this:
-- OPEN findem; NO NEED TO OPEN THE CURSOR (when using FOR)
FOR crfindem IN findem LOOP
--- FETCH findem into other1, other2, other3; FOR ALREADY DOES THIS: the values are in crfindem
l_CollectionOfRows(Counter).tmps_key := crfindem.name_of_the_first_field;
l_CollectionOfRows(Counter).tmps_cfb_rate := crfindem.name_of_the_second_field;
l_CollectionOfRows(Counter).tmps_engagement_pay_rate := crfindem.name_of_the_third_field;
Counter := Counter + 1;
END LOOP;
--- CLOSE findem; NO NEED TO CLOSE THE CURSOR (when using FOR)
Now let me add some considerations about your code (and about this example):
I don't see where you initialize your Counter variable: you MUST initialize it to 0 before entering the loop, because otherwise its initial value will be NULL and will stay null for the whole operation because (NULL + 1) evaluates again to NULL.
I don't see how your cursor is declared, so I don't know the names of the fields it extracts. in the code above I used the "fake" names name_of_the_first_field, name_of_the_second_field, name_of_the_third_field... but you must use the correct field names returned by your query
if your cursor returns some calculated value (like "select 1+2, sysdate, null from dual") you must assign a name to the calculated column to make it accessible by giving an alias to each calculated column you extract ("select 1+2 AS first_name, sysdate AS second_name, null as third_name from dual")
Edit... another info: you don't really need to declare a variable for each field even when you are explicitly using open/fetch/close: you can declare a RECORD variable (that will contain all column values with the same column names, exactly like the for loop does) by using the %ROWTYPE syntax. my example becomes like this, using %rowtype:
declare
cursor cur is select * from user_tab_comments;
-- here I am declaring a variable named c that is a RECORD variable:
-- it can contain a whole row returned by cursor cur
c cur%rowtype;
begin
open cur;
loop
fetch cur into c;
exit when cur%notfound;
DBMS_OUTPUT.PUT_LINE( c.table_name || ' - ' || c.comments);
end loop;
close cur;
end;

Related

How do I define another temporary variable and fetch into it?

Here is My code;-
CREATE OR REPLACE PROCEDURE GetDeails
(c_name VARCHAR2,
calories NUMBER)
DECLARE
CURSOR cur IS SELECT CATEGORY.Name FROM CATEGORY INNER JOIN FILLING ON CATEGORY.CategoryID = FILLING.CategoryID
WHERE c_name=FillING.Name AND calories=GramCalories;
fil cur%ROWTYPE;
BEGIN
OPEN cur;
LOOP
FETCH cur INTO fil;
EXIT WHEN (cur%NOTFOUND);
IF fil%NOTFOUND THEN
DBMS_OUTPUT.PUTLINE('NotFound');
ELSE
DBMS_OUTPUT.PUTLINE(fil.c_name, fil.calories);
END IF;
END LOOP;
CLOSE cur;
END GetDetails;
/
Basically your PROCEDURE statement is good, but having some little issues, such as :
Convert the name GetDeails to GetDetails in order to have the
matching name with the one given at the end after the last END
keyword. Indeed, using the PROCEDURE's name twice is redundant, so,
not needed.
There should be IS or AS keyword just after IN parameters' list, and the keyword DECLARE should be removed.
DBMS_OUTPUT.PUTLINE should be converted to DBMS_OUTPUT.PUT_LINE,
and two matching columns( Name and GramCalories ) should be provided in the CURSOR's SELECT list.
cursor attribute may not be applied to non-cursor FIL but to CUR
SQL> SET serveroutput ON
SQL> CREATE OR REPLACE PROCEDURE GetDetails( c_name VARCHAR2, calories NUMBER ) IS
CURSOR cur IS
SELECT f.Name, c.GramCalories
FROM CATEGORY c
JOIN FILLING f
ON f.CategoryID = c.CategoryID
WHERE c_name=f.Name
AND calories=GramCalories;
fil cur%ROWTYPE;
BEGIN
OPEN cur;
LOOP
FETCH cur INTO fil;
EXIT WHEN (cur%NOTFOUND);
IF cur%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('NotFound');
ELSE
DBMS_OUTPUT.PUT_LINE(fil.name, fil.calories);
END IF;
END LOOP;
CLOSE cur;
END;
/
The answer by #Barbaros solves most of your issue but can be further refined.
The IF statement within the loop is completely unnecessary as it will never return True when executed. If it were true the exit
statement preceding it would have exited the loop; thus no message. This is redundant; making a test where the result is already known. You could reverse the order and put the exit after the IF...END IF. But then the 'not found' message would always be produced. You can use cur%rowcount after the loop to generate the message correctly.
The dbms_output_put_line(fil.name,fil.calories) has 2 errors.
variable fil.calories does not exist. GramCalories was not selected in your original (as pointed out) nor aliased in the revision. So is not part of the cursor and thus not part of the
cursor row type.
It requires a single string parameter, as is there are 2 parameters.
Taking these into account we get:
create or replace procedure getdetails( c_name varchar2, calories number ) is
cursor cur is
select f.name, c.gramcalories
from category c
join filling f
on f.categoryid = c.categoryid
where c_name=f.name
and calories=gramcalories;
fil cur%rowtype;
begin
open cur;
loop
fetch cur into fil;
exit when (cur%notfound);
dbms_output.put_line(fil.name || ' ' || fil..gramcalories);
end loop;
if cur%rowcount = 0 then
dbms_output.put_line('Not Found');
end if;
close cur;
end getdetails;
/
As a matter of style:
Avoid the CamelCase naming convention. Oracle always folds object names to uppercase. Thus it just makes Oracle generated references difficult to read. Instead use words separated by Underscore (_).
Unlike Barbaros I do not consider using the procedure (function,
package, ...) name on the terminating end as redundant but more as a
closure as much as an 'end if'. Yes it is syntactically optional, but
optional is not the same a redundant. I always exercise that option.
So developed your style (subject to institutional/customer mandated
standards), but be consistent with it.

function not returning a value for no rows returned

I created a function that takes a movie id as input and returns stock information based from the ID. The function mostly works but if I want to retrieve information from a movie that is not in the database(returns no rows) nothing returns. Can't figure out why?
doesn't give me an error when i call an ID that returns no rows so exception handling wouldn't work.
create or replace function stock_info
(p_id IN NUMBER
)
return VARCHAR2
IS
cursor c1 is
select movie_id, movie_title, movie_qty
from mm_movie
where p_id = movie_id;
lv_movie_info VARCHAR2(100);
BEGIN
for i in c1 loop
if p_id = i.movie_id then
lv_movie_info := i.movie_title || ' is available: ' || i.movie_qty || ' on the shelf';
else
lv_movie_info := 'no data found';
end if;
end loop;
return lv_movie_info;
END STOCK_INFO;
/
The reason you don't get anything when there is no data is that the loop doesn't execute. Logically the For expression says "execute the following loop for every row returned in the cursor" but there are no rows in the cursor so it never executes the loop. Further the structure actually indicates you are expecting multiple for a given p_id. If that's not the case you can eliminate the cursor all together. Assuming p_id is the primary key you have either 0 or 1 row so:
create or replace function stock_info (p_id in number)
return text
is
lv_movie_info varchar2(100);
begin
select i.movie_title || ' is available: ' || i.movie_qty || ' on the shelf'
into lv_movie_info
from mm_movie i
where p_id = movie_id;
return lv_movie_info;
exceptions
when no_data_found
then return 'no data found';
end stock_info;
Of course if do expect more that 1 row the cursor is needed, but the IF is not as the were clause guarantees it's true. Still with 0 rows the loop will not be executed so the 'no data found' message needs to go after "End Loop".
Belayer
the cursor statement you used fetches data from the in parameter. i.e., in the cursor select you limiting based on the movie id passed.
on passing a movie id which is not in the data base, the cursor select statement would not fetch any records, and so the flow won't even go inside the for loop.
if you wanted to return no data found - on passing a movie id which is not in the database, two ways to resolve
1. before the loop, have select statement to set a flag to Y or N if exists according and to have your requirement.
2. in if not using for cursor, there is an option to check not found...
sample:
declare
cursor c1 is select * from table_sample; -- empty table
c_rec c1%rowtype;
begin
open c1;
fetch c1 into c_rec;
if c1%notfound then
dbms_output.put_line('not found');
end if;
close c1;
end;
-- output
not found

Pass an array to bind variables

I have a table that contains queries, for example:
select text from queries;
TEXT
1 select item from items where item_no between :low_item_no and :high_item_no and description <> :irellevant
The queries already contains the place holders for the bind variables.
The values themselves exists in variables table:
select * from vars;
ID NAME VALUE
1 1 low_item_no 100
2 2 high_item_no 300
3 3 irellevant_desc old
I have a package that takes the query and execute it with
execute immediate statement
but how do I bind those variables?
I don't know how much variables I have in such query, it's not static.
I wish to have a way to do something like that:
Execute immedaite my_query_str using v_array_of_vars;
Until now I don't know of a way to do something like that, only with list of variables for example:
Execute immedaite my_query_str using v_1, v_2, v_3;
Thanks!
I don't think you can do this with execute immediate as too much is unknown at compile time, so you'll have to use the dbms_sql package instead.
Here's a quick demo that gets the query and variables based on a common query ID. This assumes that the values in vars.name actually match the bind variable names in queries.text, and I haven't included any checks or error handling for that or other potential issues, or dealt with multiple select-list items or data types - just the basics:
declare
my_query_str queries.text%type;
my_cursor pls_integer;
my_result pls_integer;
my_col_descs dbms_sql.desc_tab2;
my_num_cols pls_integer;
my_item items.item%type;
begin
select text into my_query_str from queries where query_id = 42;
dbms_output.put_line(my_query_str);
-- open cursor
my_cursor := dbms_sql.open_cursor;
-- parse this query
dbms_sql.parse(my_cursor, my_query_str, dbms_sql.native);
-- bind all variables by name; assumes bind variables match vars.name
for r in (select name, value from vars where query_id = 42) loop
dbms_output.put_line('Binding ' || r.name || ' || with <' || r.value ||'>');
dbms_sql.bind_variable(my_cursor, r.name, r.value);
end loop;
my_result := dbms_sql.execute(my_cursor);
dbms_output.put_line('execute got: ' || my_result);
dbms_sql.describe_columns2(my_cursor, my_num_cols, my_col_descs);
dbms_sql.define_column(my_cursor, 1, my_item, 30); -- whatever size matches 'item'
-- fetch and do something with the results
while true loop
my_result := dbms_sql.fetch_rows(my_cursor);
if my_result <= 0 then
exit;
end if;
dbms_sql.column_value(my_cursor, 1, my_item);
dbms_output.put_line('Got item: ' || my_item);
end loop;
dbms_sql.close_cursor(my_cursor);
end;
/
You don't seem to really need an array; but if you wanted to you could create and populate an associative array as name/value pairs and then use that fir the binds.
This is just a starting point; you may have to deal with an unknown number and/or types of columns being returned, though if that's the case processing them meaningfully will be a challenge. Perhaps you need to return the result of the query as a ref cursor, which is even simpler; demo using the SQL*Plus variable and print commands:
var rc refcursor;
declare
my_query_str queries.text%type;
my_cursor pls_integer;
my_result pls_integer;
begin
select text into my_query_str from queries where query_id = 42;
dbms_output.put_line(my_query_str);
-- open cursor
my_cursor := dbms_sql.open_cursor;
-- parse this query
dbms_sql.parse(my_cursor, my_query_str, dbms_sql.native);
-- bind all variables by name; assumes bind variables match vars.name
for r in (select name, value from vars where query_id = 42) loop
dbms_output.put_line('Binding ' || r.name || ' || with <' || r.value ||'>');
dbms_sql.bind_variable(my_cursor, r.name, r.value);
end loop;
my_result := dbms_sql.execute(my_cursor);
dbms_output.put_line('execute got: ' || my_result);
:rc := dbms_sql.to_refcursor(my_cursor);
end;
/
print rc
Notice you don't close the cursor inside the PL/SQL block in this scenario.
You could also convert to a ref cursor and then fetch from that within your procedure - there's a bulk-collect example in the docs - but again you'd need to know the number and types of the select-list items to do that.

If condition in PL/SQL script with cursor and loop

I would like to ask for some help or advice in this particular case.
I have table called "Teams". The table contains 3 columns - Issue, Responsible_team and More_Info (all varchar2).
I have a PL/SQL script with cursor and loop for selecting as many teams as issue description you type (some random word you think it might help you find the responsible team). This part works great for me.
But I do not know how to compile the IF condition in there. If no team is found according to typed word description, I would like to get some basic output dbms_output.put_line('Responsible team is not found').
There are 2 ways how I wrote the script. Classic loop and while loop.
I would be happy for any advice.
1.script
set verify off
DECLARE
v_issue teams.issue%type; --variable for issue column from teams table
v_respteam teams.responsible_team%type; --variable for responsible_team column from teams table
v_info teams.more_info%type; --variable for more_info column from teams table
--cursor declaration
CURSOR c_respteam
RETURN teams%ROWTYPE
IS
SELECT issue, responsible_team, more_info
FROM teams
WHERE lower(issue) like '%&Describe_Issue%';
BEGIN
OPEN c_respteam;
LOOP
FETCH c_respteam into v_issue, v_respteam, v_info;
EXIT when c_respteam%NOTFOUND;
dbms_output.put_line('Responsible team is '|| v_respteam || ' --> ' || v_info);
END LOOP;
CLOSE c_respteam;
end;
/
2.script
-- cursor with while loop
set verify off
DECLARE
v_issue teams.issue%type; --variable for issue column from teams table
v_respteam teams.responsible_team%type; --variable for responsible_team column from teams table
v_info teams.more_info%type; --variable for more_info column from teams table
CURSOR c_respteam
RETURN teams%ROWTYPE IS
SELECT issue, responsible_team, more_info
FROM teams
WHERE lower(issue) like '%&Describe_Issue%';
BEGIN
OPEN c_respteam;
FETCH c_respteam INTO v_issue, v_respteam, v_info;
WHILE c_respteam%FOUND
LOOP
dbms_output.put_line('Responsible team is '|| v_respteam || ' --> ' || v_info);
FETCH c_respteam INTO v_issue, v_respteam, v_info;
END LOOP;
CLOSE c_respteam;
END;
/
You could rewrite to:
declare
l_found boolean :=false;
cursor c_respteam is
select issue
,responsible_team
,more_info
from teams
where lower(issue) like '%&Describe_Issue%';
begin
for r in c_respteam
loop
l_found := true;
dbms_output.put_line('Responsible team is ' || r.responsible_team || ' --> ' || r.more_info);
end loop;
if not l_found
then
dbms_output.put_line('No records found');
end if;
end;
/
You need to have a counter variable [ETA: ooh, I like Rene's boolean variable idea instead; either way, you need an extra variable!] to work out if any rows were returned or not. I'm not sure why you're using an explicit cursor fetch, rather than using the cursor-for-loop? Cursor-for-loops are not only easier to write, read and maintain, but Oracle have put some behind-the-scenes optimisation in, to aid performance.
Of course, depending on what you're actually doing with the data returned by your cursor (dbms_output.put_line being something that you should never have in your production code), it's debatable that you would need to loop through a cursor at all.
Anyway, with that said, here's an example demonstrating how I would handle your requirement to check for no rows returned by the cursor:
declare
cursor cur (p_val varchar2)
is
select dummy
from dual
where dummy like '%'||p_val||'%';
v_counter integer := 0;
begin
for rec in cur('Y')
loop
dbms_output.put_line('value of dummy = '||rec.dummy);
v_counter := v_counter + 1;
end loop;
if v_counter = 0 then
dbms_output.put_line('no rows returned');
end if;
end;
/
no rows returned
declare
cursor cur (p_val varchar2)
is
select dummy
from dual
where dummy like '%'||p_val||'%';
v_counter integer := 0;
begin
for rec in cur('X')
loop
dbms_output.put_line('value of dummy = '||rec.dummy);
v_counter := v_counter + 1;
end loop;
if v_counter = 0 then
dbms_output.put_line('no rows returned');
end if;
end;
/
value of dummy = X
To expand on what I said in my comment below, it sounds like you just need a single sql statement, rather than using PL/SQL and relying on dbms_output.
Eg., say you have the following statement:
select lvl
from (select 'X'||level lvl from dual connect by level <= 10)
where lvl like '%&val%';
with &val blank, you get:
LVL
-----------------------------------------
X1
X2
X3
X4
X5
X6
X7
X8
X9
X10
With &val = 2 you get:
LVL
-----------------------------------------
X2
With &val = 100 you get:
no rows selected.

How to find number of rows in cursor

I would like to find the number of rows in a cursor. Is there a keyword that can help? Using COUNT, we have to write a query. Any help will be greatly appreciated.
The cursor_variable.%ROWCOUNT is the solution. But its value will be 0 if you check it after opening. You need to loop through all the records, to get the total row count. Example below:
DECLARE
cur sys_refcursor;
cur_rec YOUR_TABLE%rowtype;
BEGIN
OPEN cur FOR
SELECT * FROM YOUR_TABLE;
dbms_output.put_line(cur%rowcount);--returning 0
LOOP
FETCH cur INTO cur_rec;
EXIT WHEN cur%notfound;
dbms_output.put_line(cur%rowcount);--will return row number beginning with 1
dbms_output.put_line(cur_rec.SOME_COLUMN);
END LOOP;
dbms_output.put_line('Total Rows: ' || cur%rowcount);--here you will get total row count
END;
/
You must open the cursor and then fetch and count every row. Nothing else will work.
You can also use BULK COLLECT so that a LOOP is not needed,
DECLARE
CURSOR c
IS SELECT *
FROM employee;
TYPE emp_tab IS TABLE OF c%ROWTYPE INDEX BY BINARY_INTEGER;
v_emp_tab emp_tab;
BEGIN
OPEN c;
FETCH c BULK COLLECT INTO v_emp_tab;
DBMS_OUTPUT.PUT_LINE(v_emp_tab.COUNT);
CLOSE c;
END;
/
Edit: changed employee%ROWTYPE to c%ROWTYPE
You can use following simple single line code to print cursor count
dbms_output.put_line(TO_CHAR(cur%rowcount));
This should work for you
DECLARE
CURSOR get_data_ IS
SELECT *
FROM table_abc_
WHERE owner = user_; -- your query
counter_ NUMBER:= 0;
BEGIN
FOR data_ IN get_data_ LOOP
counter_ := counter_ + 1;
END LOOP;
dbms_output.put_line (counter_);
END;
DECLARE #STRVALUE NVARCHAR(MAX),
#CREATEDDATE DATETIME,
#STANTANCEVALUE NVARCHAR(MAX),
#COUNT INT=0,
#JOBCODE NVARCHAR(50)='JOB00123654',
#DATE DATETIME=GETDATE(),
#NAME NVARCHAR(50)='Ramkumar',
#JOBID INT;
CREATE TABLE #TempContentSplitValue (ITEMS NVARCHAR(200))
SELECT #JOBID = i.Id FROM JobHeader_TBL i WHERE Id=1201;
IF EXISTS (SELECT 1 FROM JobHeader_TBL WHERE Id=#JOBID)
BEGIN
SELECT #STRVALUE= Description from ContentTemplate_TBL where Id=1
INSERT INTO #TempContentSplitValue SELECT * FROM dbo.split(#STRVALUE, '_')
SET #STRVALUE=''
DECLARE db_contentcursor CURSOR FOR SELECT ITEMS FROM #TempContentSplitValue
OPEN db_contentcursor
FETCH NEXT FROM db_contentcursor
INTO #STANTANCEVALUE
WHILE (##FETCH_STATUS = 0)
BEGIN
SET #STRVALUE += #STANTANCEVALUE + 'JOB00123654'
SET #COUNT += 1
SELECT #COUNT
FETCH NEXT FROM db_contentcursor INTO #STANTANCEVALUE
END
CLOSE db_contentcursor
DEALLOCATE db_contentcursor
DROP TABLE #TempContentSplitValue
SELECT #STRVALUE
END
Here I am trying to count the total number of customers with age greater than 25. So store the result in the cursor first. Then count the size of the cursor inside the function or in the main begin itself.
DECLARE
cname customer24.cust_name%type;
count1 integer :=0;
CURSOR MORETHAN is
SELECT cust_name
FROM customer24
where age>25;
BEGIN
OPEN MORETHAN;
LOOP
FETCH MORETHAN into cname;
count1:=count1+1;
EXIT WHEN MORETHAN%notfound;
END LOOP;
-- dbms_output.put_line(count1);
dbms_output.put_line(MORETHAN%ROWCOUNT);
CLOSE MORETHAN;
END;
There is a possible work around that may be useful/needed because of the overhead of accessing a database server over a network (e.g., when using Ajax calls)
Consider this:
CURSOR c_data IS
SELECT per_first_name , null my_person_count
FROM person
UNION
SELECT null as per_first_name , count( distinct per_id ) as my_person_count
FROM person
order by my_person_count ;
The first row fetched has the count of records. One MUST add specific columns fetched (the use of the * does not work), and one can add additional filters.
Try this:
print(len(list(cursor)))
I always read that people loop through results. Why not using a count(*)?
An example from my production code:
PROCEDURE DeleteStuff___(paras_ IN Parameters_Type_Rec)
IS
CURSOR findEntries_ IS
select * from MyTable
where order_no = paras_.order_no;
counter_ NUMBER;
CURSOR findEntries_count_ IS
SELECT COUNT(*) from MyTable
where order_no = paras_.order_no;
BEGIN
OPEN findEntries_count_;
FETCH findEntries_count_ INTO counter_;
CLOSE findEntries_count_;
dbms_output.put_line('total records found: '||counter_);
IF (counter_ = 0) THEN
-- log and leave procedure
RETURN;
END IF;
FOR order_rec_ IN findEntries_ LOOP
EXIT WHEN findEntries_%NOTFOUND OR findEntries_%NOTFOUND IS NULL;
-- do stuff - i.e. delete a record.
API_Package.Delete(order_rec_);
END LOOP;
END DeleteStuff___;
If the query is small, that is my prefered way.
In this example, I just want to know (and log) how many entries I'll delete.
p.s. Ignore the three underlines. In IFS, this is used when you want private procedures or functions.
You can’t have cursor count at start. For that you need to fetch complete cursor; that is the way get cursor count.
declare
cursor c2 is select * from dept;
var c2%rowtype;
i number :=0;
begin
open c2;
loop
fetch c2 into var;
exit when c2%NOTFOUND;
i: = i+1;
end loop;
close c2;
dbms_output.put_line('total records in cursor'||i);
end;
You can use %ROWCOUNT attribute of a cursor.
e.g:
DECLARE
CURSOR lcCursor IS
SELECT *
FROM DUAL;
BEGIN
OPEN lcCursor ;
DBMS_OUTPUT.PUT_LINE(lcCursor%ROWCOUNT);
CLOSE lcCursor ;
END;

Resources