If condition in PL/SQL script with cursor and loop - oracle

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.

Related

For loop not printing last record in a cursor

So,I am working with a cursor,the cursor was initially operating with the initial for loop,now i needed to perform operation if the no of records in the cursor is more than one,so i fetched the no of records first and stored in a variable and used an if-condition based on that.Now the problem is when I run the whole process,the procedure does its job,but only for the first record in the cursor and entirely skips the second record.Please suggest or help me identify the mistake.
Adding code snippet.
for m in get_m_p(a,b)--main cursor
loop
fetch get_m_p into c_m;
exit when g_m_p%notfound;
end loop;
tempcount := g_m_p%ROWCOUNT:
statements---
if(tempcount>1) then
statements----
end if;
end loop;
for the two records the main curosr is returning in first line,operations are only done for the first one,and the second record is being skipped entirely.
This is a superfluous line:
fetch get_m_p into c_m;
You don't explicitly fetch within a cursor FOR loop, it is implicitly done in each loop iteration. Remove that line.
How to get number of rows returned by a cursor? Lucky you, it seems that you don't care whether how many rows - exactly - it returns. All you want to know is whether it returned more than 1 row. So, count them, literally; exit the loop if counter exceeds 1.
SQL> DECLARE
2 CURSOR get_m_p IS
3 SELECT *
4 FROM emp
5 WHERE deptno = 10;
6
7 l_cnt NUMBER := 0;
8 BEGIN
9 FOR m IN get_m_p
10 LOOP
11 l_cnt := l_cnt + 1;
12 EXIT WHEN l_cnt > 1;
13 END LOOP;
14
15 DBMS_OUTPUT.put_line ('Cursor returned at least ' || l_cnt || ' row(s)');
16
17 IF l_cnt > 1
18 THEN
19 NULL;
20 -- the rest of statements go here
21 END IF;
22 END;
23 /
Cursor returned at least 2 row(s)
PL/SQL procedure successfully completed.
SQL>
As there's no way to know how many rows will cursor return, unfortunately, you'll have to check that first, and then decide what to do with the result.
DECLARE
CURSOR get_m_p IS
SELECT *
FROM emp
WHERE deptno = 10;
l_cnt NUMBER := 0;
BEGIN
SELECT COUNT (*)
INTO l_cnt
FROM (-- this is cursor's SELECT statement
SELECT *
FROM emp
WHERE deptno = 10);
FOR m IN get_m_p
LOOP
-- some statements here
IF l_cnt > 1
THEN
NULL;
-- statements to be executed if cursor return more than 1 row
END IF;
END LOOP;
END;
/
Cursor:
Oracle creates memory area to process SQL statement which is called context area and the cursor is pointer to the context area. A cursor holds the rows (one or more) returned by a SQL statement. The set of rows the cursor holds is referred to as the active set.
There are two type of cursor
1. Implicit cursor
2. Explicit cursor
Implicit Cursors :
Implicit cursors are automatically created by Oracle whenever an SQL statement is executed. Any SQL cursor attribute will be accessed as sql%attribute_name as shown below in the example. Use the SQL%ROWCOUNT attribute to determine the number of rows affected
DECLARE
no_of_records number(2);
BEGIN
select * from records;
IF sql%notfound THEN
dbms_output.put_line('no records present');
ELSIF sql%found THEN
no_of_records := sql%rowcount;
IF no_of_records > 1 THEN
dbms_output.put_line('no of records ' || no_of_records);
END IF
END IF;
END;
Explicit Cursors :
Explicit cursors are programmer-defined cursors for gaining more control over the context area. An explicit cursor should be defined in the declaration section of the PL/SQL Block. It is created on a SELECT Statement which returns more than one row.
Please see below example:
DECLARE
r_id records.id%type;
CURSOR c_records is
SELECT id FROM records;
BEGIN
OPEN c_records;
LOOP
FETCH c_records into r_id;
EXIT WHEN c_records%notfound;
dbms_output.put_line('Record id ' || r_id );
END LOOP;
CLOSE c_records;
END;
Reference :
https://www.tutorialspoint.com/plsql/plsql_cursors.htm
As an alternative you can cache every row and process after.
Example using sample schema "HR" on Oracle 11g Express Edition:
DECLARE
CURSOR get_m_p
IS
SELECT *
FROM hr.employees
WHERE department_id = 60
order by employee_id;
--
rcEmp_last get_m_p%rowtype;
l_cnt NUMBER;
BEGIN
FOR rcM IN get_m_p LOOP
l_cnt := get_m_p%rowcount;
Dbms_Output.Put_Line('l_cnt='||l_cnt);
if l_cnt=1 then
rcEmp_last:=rcM;
Else
Dbms_Output.Put_Line('Process='||to_char(l_cnt-1));
Dbms_Output.Put_Line('rcEmp_last.employee_id='||rcEmp_last.employee_id);
--
rcEmp_last:=rcM;
END IF;
End loop;
--
Dbms_Output.Put_Line('Exited FOR-LOOP');
Dbms_Output.Put_Line('l_cnt='||l_cnt);
--
if l_cnt>1 then
Dbms_Output.Put_Line('rcEmp_last.employee_id='||rcEmp_last.employee_id);
End if;
END;
Output:
Text
PL/SQL block, executed in 1 ms
l_cnt=1
l_cnt=2
Process=1
rcEmp_last.employee_id=103
l_cnt=3
Process=2
rcEmp_last.employee_id=104
l_cnt=4
Process=3
rcEmp_last.employee_id=105
l_cnt=5
Process=4
rcEmp_last.employee_id=106
Exited FOR-LOOP
l_cnt=5
rcEmp_last.employee_id=107
Total execution time 35 ms

Anonymous block errors with "ORA-01422: exact fetch returns more than requested number of rows" [duplicate]

This question already has an answer here:
PL/SQL ORA-01422: exact fetch returns more than requested number of rows
(1 answer)
Closed 6 years ago.
This is my code:
declare
name1 varchar2(25);
code number(3):=1;
begin
while(code<10)
loop
select attrname into name1 from catregionmap where attrtype=code;
dbms_output.put_line('The name for' || name1 || 'ways' );
code:= code+1;
end loop;
end;
But I get this error:
"ORA-01422: exact fetch returns more than requested number of rows"
How can I fix this?
You can always use a Cursor. The main reason why a cursor is used is in order to avoid the problem / issue faced by you. For the code from which you have received the oracle error "Too many" We can simply re write the code by using a simple cursor as given below (You have hard coded the value of attrtype between 1 and 10).
DECLARE
name1 VARCHAR2(25);
code NUMBER(3) := 1;
CURSOR c1 IS
SELECT attrname
FROM catregionmap
WHERE attrtype <= 10;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO name1;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.put_line('The name for' || name1 || 'ways');
code := code + 1;
END LOOP;
CLOSE c1;
END;
Please make a note of yourself that cursors are effectively used when number of records are being fetched from the select query. The code given by my friend XING is also correct as BULK collect is a way of avoiding cursor usage. I would suggest you to read the PLSQL manual better and gain knowledge in cursor and Bulk collect options.
Hope this would help you out. Thanks.!!!
The issue states that your select statement is returning more than 1 rows and you are trying to store many rows to a single variable name1 . In that case you need a collection to store your values.
Use this way:
DECLARE
type name11 is table of catregionmap.attrname%type index by pls_integer;
name1 name11;
code NUMBER (3) := 1;
BEGIN
WHILE (code < 10)
LOOP
SELECT attrname
BULK COLLECT INTO name1
FROM catregionmap
WHERE attrtype = code;
for rec in 1..name1.count
loop
DBMS_OUTPUT.put_line ('The name for' || name1(rec) || 'ways');
end loop;
code := code + 1;
END LOOP;
END;

ORA-01007 "variable not in select list" from dbms_sql.column_value call

I am trying to use dynamic SQL to sample all the data in a schema with a pattern:
DECLARE
xsql varchar2(5000);
c NUMBER;
d NUMBER;
col_cnt INTEGER;
f BOOLEAN;
rec_tab DBMS_SQL.DESC_TAB;
col_num NUMBER;
varvar varchar2(500);
PROCEDURE print_rec(rec in DBMS_SQL.DESC_REC) IS
BEGIN
DBMS_OUTPUT.ENABLE(1000000);
DBMS_OUTPUT.NEW_LINE;
DBMS_OUTPUT.PUT_LINE('col_type = '
|| rec.col_type);
DBMS_OUTPUT.PUT_LINE('col_maxlen = '
|| rec.col_max_len);
DBMS_OUTPUT.PUT_LINE('col_name = '
|| rec.col_name);
DBMS_OUTPUT.PUT_LINE('col_name_len = '
|| rec.col_name_len);
DBMS_OUTPUT.PUT_LINE('col_schema_name = '
|| rec.col_schema_name);
DBMS_OUTPUT.PUT_LINE('col_schema_name_len = '
|| rec.col_schema_name_len);
DBMS_OUTPUT.PUT_LINE('col_precision = '
|| rec.col_precision);
DBMS_OUTPUT.PUT_LINE('col_scale = '
|| rec.col_scale);
DBMS_OUTPUT.PUT('col_null_ok = ');
IF (rec.col_null_ok) THEN
DBMS_OUTPUT.PUT_LINE('true');
ELSE
DBMS_OUTPUT.PUT_LINE('false');
END IF;
END;
BEGIN
c := DBMS_SQL.OPEN_CURSOR;
xsql:='
WITH got_r_num AS
(
SELECT e.* -- or whatever columns you want
, ROW_NUMBER () OVER (ORDER BY dbms_random.value) AS r_num
FROM dba_tab_columns e
)
SELECT * -- or list all columns except r_num
FROM got_r_num
WHERE r_num <= 10';
DBMS_SQL.PARSE(c, xsql, DBMS_SQL.NATIVE);
d := DBMS_SQL.EXECUTE(c);
DBMS_SQL.DESCRIBE_COLUMNS(c, col_cnt, rec_tab);
LOOP
IF DBMS_SQL.FETCH_ROWS(c)>0 THEN
NULL;
-- get column values of the row
DBMS_SQL.COLUMN_VALUE(c, 2, varvar);
--dbms_output.put_line('varvar=');
--DBMS_SQL.COLUMN_VALUE(source_cursor, 2, name_var);
--DBMS_SQL.COLUMN_VALUE(source_cursor, 3, birthdate_var);
-- Bind the row into the cursor that inserts into the destination table. You
-- could alter this example to require the use of dynamic SQL by inserting an
-- if condition before the bind.
--DBMS_SQL.BIND_VARIABLE(destination_cursor, ':id_bind', id_var);
--DBMS_SQL.BIND_VARIABLE(destination_cursor, ':name_bind', name_var);
--DBMS_SQL.BIND_VARIABLE(destination_cursor, ':birthdate_bind',
--birthdate_var);
--ignore := DBMS_SQL.EXECUTE(destination_cursor);
--ELSE
-- No more rows to copy:
--EXIT;
END IF;
END LOOP;
--EXIT WHEN d != 10;
--END LOOP;
col_num := rec_tab.first;
IF (col_num IS NOT NULL) THEN
LOOP
print_rec(rec_tab(col_num));
col_num := rec_tab.next(col_num);
EXIT WHEN (col_num IS NULL);
END LOOP;
END IF;
DBMS_SQL.CLOSE_CURSOR(c);
END;
/
When I run that it gives me this error from the line with the dbms_sql.column_value call:
ORA-01007: variable not in select list
If I comment out that dbms_sql.column_value call it still errors but now with:
ORA-01002: fetch out of sequence
What am I doing wrong?
You have two problems in the code you posted. Firstly you have skipped part of the execution flow because you haven't called the DEFINE_COLUMN procedure. That is what is causing the ORA-01007 error, as the dynamic SQL processing hasn't been told about the select list columns via that call. For your current code you only need to define column 2, but assuming you will actually want to refer to the others you can define them in a loop. To treat them all as string for display you could do:
...
DBMS_SQL.PARSE(c, xsql, DBMS_SQL.NATIVE);
d := DBMS_SQL.EXECUTE(c);
DBMS_SQL.DESCRIBE_COLUMNS(c, col_cnt, rec_tab);
FOR i IN 1..col_cnt
LOOP
-- dbms_output.put_line('col_name is ' || rec_tab(i).col_name);
DBMS_SQL.DEFINE_COLUMN(c, i, varvar, 500);
END LOOP;
LOOP
IF DBMS_SQL.FETCH_ROWS(c)>0 THEN
...
If you want to do anything that needs to treat the variables as the right types you could have a local variable of each type and use the data type from the rec_tab information you already have from describe_columns to use the appropriately typed variable for each column.
The second problem, which you were hitting when you commented the column_value call, is still there once that definbe issue has been fixed. Your loop doesn't ever exit, so after you fetch the last row from the cursor you do a further invalid fetch, which throws ORA-01002. You have the code to avoid that already but it's commented out:
...
LOOP
IF DBMS_SQL.FETCH_ROWS(c)>0 THEN
-- get column values of the row
DBMS_SQL.COLUMN_VALUE(c, 2, varvar);
...
ELSE
-- No more rows to copy:
EXIT;
END IF;
END LOOP;
...
With those two changes your code runs, and dumps the view structure:
PL/SQL procedure successfully completed.
col_type = 1
col_maxlen = 30
col_name = OWNER
col_name_len = 5
col_schema_name =
col_schema_name_len = 0
col_precision = 0
col_scale = 0
col_null_ok = false
col_type = 1
col_maxlen = 30
col_name = TABLE_NAME
...
To those who find this question when accessing Oracle through ODP.NET, as I did:
We started getting this error whenever we would add column to an existing table in our application. I'm not sure what all the conditions were to make it fail, but ours were:
Run a SELECT * FROM "table".
Include a ROWNUM restriction in the WHERE clause (WHERE ROWNUM < 10).
Run that through the ODP.NET dataReader.GetSchemaTable() call.
Running unrestricted queries or running queries directly on Oracle SQL Developer did not seem to cause the error.
I've hit some pretty weird stuff in the past with Oracle connection pooling, so I eventually thought that could be the problem. The solution was to restart the web service to force all the connections to be fully dropped and recreated.
The theory is that the ODP.NET connection from the connection pool still had no idea the column existed on the table, but the column was returned by the database.

PLS-00103: Encountered the symbol "1" when expecting one of the following: (

Wrote the following PLSQL script to produce a report and am getting the error messages
Error at line 5
ORA-06550: line 61, column 18:
PLS-00103: Encountered the symbol "1" when expecting one of the following:
(
I've been through the code many times and I cannot find the error. Any help would be greatly appreciated.
I'm currently working in Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
SET serveroutput ON size 1000000;
DECLARE
TYPE TITLE_RECORD_TYPE IS RECORD
(id number(19),
gaid varchar2(20),
artist_legal_name varchar2(510),
artist_display_title varchar2(510),
display_title varchar2(510),
category varchar2(255),
type varchar2(255),
sub_type varchar2(255));
TITLE_RECORD TITLE_RECORD_TYPE;
v_title varchar2(510);
v_artist varchar2(510);
v_total_rows_error number(20) := 0;
v_row_count number(10) := 0;
v_error_desc varchar2(200) := NULL;
v_error_code number(19);
CURSOR ARTIST_TITLE_CURSOR is
select track_artist,track_title
from asset_artist_title;
CURSOR QUERY_CURSOR is
select distinct g1.gaid,g2.legal_name,g1.artist_display_title,
g1.display_title,g1.category,g1.type,g1.sub_type
from gcdm_app_rpt.rpt_asset g1,
gcdm_app_rpt.rpt_artist g2
where g1.artist_id = g2.id
and g1.is_deleted <> 'Y'
and g1.is_core = 'Y'
and g2.is_core = 'Y'
and g1.title like v_title||'%'
and g1.artist_display_title like v_artist||'%';
BEGIN
OPEN ARTIST_TITLE_CURSOR;
LOOP
FETCH ARTIST_TITLE_CURSOR into v_artist,v_title;
EXIT WHEN ARTIST_TITLE_CURSOR%NOTFOUND or ARTIST_TITLE_CURSOR%NOTFOUND IS NULL;
SELECT count(*)
INTO v_row_count
FROM gcdm_app_rpt.rpt_asset g1,
gcdm_app_rpt.rpt_artist g2
WHERE g1.artist_id = g2.id
AND g1.is_core = 'Y'
AND g1.is_deleted <> 'Y'
AND g2.is_core = 'Y'
AND g1.title like v_title||'%'
AND g1.artist_display_title like v_artist||'%';
IF v_row_count < 1 THEN
v_error_desc := 'Matching Asset record for '||v_artist||' - '||v_title||' not found';
DBMS_OUTPUT.PUT_LINE('Error: '||v_error_desc||'.');
v_row_count := 0;
v_total_rows_error := v_total_rows_error + 1;
ELSE
OPEN QUERY_CURSOR
FOR i in 1..ARTIST_TITLE_CURSOR
LOOP
FETCH QUERY_CURSOR into TITLE_RECORD;
EXIT WHEN QUERY_CURSOR%NOTFOUND or QUERY_CURSOR%NOTFOUND IS NULL;
DBMS_OUTPUT.PUT_LINE(title_record.id,title_record.gaid,title_record.artist_legal_name,title_record.artist_display_name,
title_record.display_title,title_record.category,title_record.type,title_record.sub_type);
END LOOP;
CLOSE QUERY_CURSOR;
v_row_count := 0;
END IF;
END LOOP;
CLOSE ARTIST_TITLE_CURSOR;
DBMS_OUTPUT.PUT_LINE(chr(0));
IF v_total_rows_error > 0 THEN
DBMS_OUTPUT.PUT_LINE('Total Rows in error: '||v_total_rows_error);
END IF;
DBMS_OUTPUT.PUT_LINE(CHR(0));
EXCEPTION
WHEN OTHERS THEN
v_error_desc := SQLERRM;
v_error_code := SQLCODE;
DBMS_OUTPUT.PUT_LINE('Error: '||v_error_desc||' - '||v_error_code);
END;
It's line 67 in what you've posted, not 61, but still; this line is not right:
FOR i in 1..ARTIST_TITLE_CURSOR
You're trying to loop over a range of numbers - perhaps you wanted the number of records returned by the cursor, which you can't get - but your end 'number' is a cursor, so not legal in that context.
But it seems to be completely out of place anyway as you're looping over the QUERY_CURSOR records, so I wouldn't think the ARTIST_TITLE_CURSOR is relevant at this point. And you aren't attempting to use i. It looks like you can just remove that line.
More importantly, the previous line is missing a semi-colon:
OPEN QUERY_CURSOR;
Because it doesn't have one it's seeing the FOR and expecting a cursor query.
Following up on comments about why you have that FOR 1..v_row_count, it's still a bit redundant. You're limiting the number of fetches you do to match the count you got previously, from essentially the same query as you have in the cursor, which means you don't quite ever hit the EXIT WHEN QUERYCURSOR%NOTFOUND condition - that would come from the v_row_count+1 loop iteration. Normally you wouldn't know how many rows you expect to see before you loop over a cursor.
You don't really need to know here. The count query is repetitive - you're querying the same data you then have to hit again for the cursor, and you have to maintain the query logic in two places. It would be simpler to forget the count step, and instead keep a counter as you loop over the cursor; then handle the zero-rows condition after the loop. For example:
DECLARE
...
BEGIN
OPEN ARTIST_TITLE_CURSOR;
LOOP
FETCH ARTIST_TITLE_CURSOR into v_artist,v_title;
EXIT WHEN ARTIST_TITLE_CURSOR%NOTFOUND;
-- initialise counter for each ARTIST_TITLE
v_row_count := 0;
OPEN QUERY_CURSOR;
LOOP
FETCH QUERY_CURSOR into TITLE_RECORD;
EXIT WHEN QUERY_CURSOR%NOTFOUND;
-- increment 'found' counter
v_row_count := v_row_count + 1;
DBMS_OUTPUT.PUT_LINE(title_record.id
||','|| title_record.gaid
||','|| title_record.artist_legal_name
||','|| title_record
||','|| artist_display_name
||','|| title_record.display_title
||','|| title_record.category
||','|| title_record.type
||','|| title_record.sub_type);
END LOOP;
CLOSE QUERY_CURSOR;
-- now check if we found anything in the QUERY_CURSOR loop
IF v_row_count < 1 THEN
v_error_desc := 'Matching Asset record for '||v_artist||' - '||v_title||' not found';
DBMS_OUTPUT.PUT_LINE('Error: Matching Asset record for '
|| v_artist || ' - ' || v_title || ' not found.');
v_total_rows_error := v_total_rows_error + 1;
END IF;
END LOOP;
CLOSE ARTIST_TITLE_CURSOR;
--DBMS_OUTPUT.PUT_LINE(chr(0));
-- presumably this was meant to put out a blank line; use this instead
DBMS_OUTPUT.NEW_LINE;
IF v_total_rows_error > 0 THEN
DBMS_OUTPUT.PUT_LINE('Total Rows in error: '||v_total_rows_error);
END IF;
--DBMS_OUTPUT.PUT_LINE(CHR(0));
DBMS_OUTPUT.NEW_LINE;
END;
I've also taken out the exception handler because it isn't really adding anything; you'd see the code and message without it, even if you didn't have server output on; and catching WHEN OTHERS is a bad habit to get into.
You also don't need to declare your record type. You could use an implicit cursor anyway and avoid the type and variable completely, but even with the cursor definition you have, you could put this afterwards instead:
TITLE_RECORD QUERY_CURSOR%ROWTYPE;
There are various ways to open and loop over cursors, and you're using one of the more explicit ones - which isn't a bad thing for learning about them, but be aware of the options too.

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