Reputation: 37
The following query via PL/SQL returns expected results:
select member_id, birth_date from ( select * from member order by member_id asc ) where birth_date > 0 and update_end_date = 29991231 and rownum <= 20;
It returns 20 rows of data, ordered like I want it to.
However when I incorporate a variant of that query within a while loop inside of a SQL block, it errors on the second iteration of the loop:
set serveroutput on;
declare
dob number (8);
currentDate number (8);
mID number (8);
members integer;
i integer;
begin
select to_char(sysdate, 'YYYYMMDD') into currentDate from dual;
dbms_output.put_line('Current Date: ' || currentDate);
select count(*) into members from member where birth_date > 0 and update_end_date = 29991231;
dbms_output.put_line('Member Records: ' || members);
i := 1;
while i <= 10
loop
select member_id, birth_date into mID, dob from ( select * from member order by member_id asc ) where birth_date > 0 and update_end_date = 29991231 and rownum = i;
dbms_output.put_line('Row number: ' || i || ' > Member ID: ' || mID || ' | Member DOB: ' || dob);
i := i + 1;
end loop;
end;
/
The idea is to start at the first row of the ordered results, then iterate through each row, outputting that row's results.
Eventually once I have this working, I intend to compare this data with an expected value and perform more conditional logic after that.
So unless I'm missing a fundamental "Oracle Thing" then I don't see any good reason as to why this logic fails.
I do recognize that I'm using more of a general-purpose scripting language approach to perform my logic as opposed to nested Oracle statements, but I don't see a reason why that should make a difference.
Again, could be some ignorance at play here.
Thank you in advance.
Upvotes: 1
Views: 63
Reputation: 191275
The problem is with how rownum
works. You can only use rownum <
or rownum <=
, or rownum = 1
; you can't compare directly to anything other than 1, or use the >
or >=
operators.
From Tom Kyte's article:
A ROWNUM value is assigned to a row after it passes the predicate phase of the query but before the query does any sorting or aggregation. Also, a ROWNUM value is incremented only after it is assigned, which is why the following query will never return a row:
select * from t where ROWNUM > 1;
So your main query with rownum <= 20
is fine, and in the first iteration of your loop where rownum = 1
is also OK. But on the second iteration rownum = 2
can never actually be true, so you get a no-data-found error.
Using your own variable for a loop iterator when you have a result set is doing more work than you need to. You could make it work by including the rownum
in a subquery, and then filtering that in an outer query, but it won't be very pretty or efficient. You can get what you need, I think, from:
declare
currentDate number (8);
begin
select to_char(sysdate, 'YYYYMMDD') into currentDate from dual;
dbms_output.put_line('Current Date: ' || currentDate);
for r in (
select member_id as mID, birth_date as dob, rownum as rn
from (
select * from member order by member_id asc
)
where birth_date > 0
and update_end_date = 29991231
and rownum <= 20
)
loop
dbms_output.put_line('Row number: ' || r.rn
|| ' > Member ID: ' || r.mID
|| ' | Member DOB: ' || r.dob);
end loop;
end;
/
This only executes the query once, in the cursor, rather than partially repeating it for each value of i
. Read more about cursors.
I've included the rownum
(with an alias so you can refer to it later) which has the same value as your i
would have. You can do anything you want with the selected values inside the loop, referring to them with the r.
prefix (use a more meaningful name!) and the column name/alias from the cursor query.
Storing or manipulating dates as numbers (or strings) is not a good idea though, and a bad habit if you're just starting out. Always use the correct data type.
Upvotes: 3
Reputation: 379
It's quite possible there are not enough records in the result set to complete the loop. For instance, if you only have two records, asking for a record where rownum = 5 would give you a NO_DATA_FOUND exception. You could catch this in an exception block:
BEGIN
i := 1;
while i <= 10
loop
select member_id, birth_date into mID, dob from ( select * from member order by member_id asc ) where birth_date > 0 and update_end_date = 29991231 and rownum = i;
dbms_output.put_line('Row number: ' || i || ' > Member ID: ' || mID || ' | Member DOB: ' || dob);
i := i + 1;
end loop;
EXCEPTION WHEN NO_DATA_FOUND THEN NULL;
END;
Change the 'NULL' to handle the exception, but this should just exit out of the loop for you.
Upvotes: 0