问题
Please find the below code. It runs non stop with no operations or output.
what I'm doing wrong.
declare
cid number;
cadd number;
ctras number;
cr varchar(2);
cad number;
cursor c1 IS
select c_tras, c_id, c_add from customer_master;
cursor c2 IS
select c_address, cr from customer_address where c_id = cid;
begin
open c1;
open c2;
LOOP
fetch c1 into ctras, cid, cadd;
fetch c2 into cad, cr;
if cr='N'
THEN
update customer_master set c_address = (select c_address from customer_address where cr = 'Y' and c_id = cid) where c_tras = ctras;
END IF;
END LOOP;
END;
/`
回答1:
As @pcej answered you are missing some parts of handling cursors.
In general, it is normally only some few specific cases where it is a good idea to handle cursors explicitly in your own code. In almost all cases it is a better idea to preferably do things in a single SQL statement if at all possible, or if looping is actually needed then you use implicit cursor FOR loops, where the PL/SQL engine does all the cursor-handling for you.
Let's simplify your code step by step to show you what can be done...
First see how you can avoid declaring cursors, fetching into variables, and handling EXITs. This you can do by using the FOR loop:
begin
for c1 in (
select cm.c_tras, cm.c_id, cm.c_add
from customer_master cm
) loop
for c2 in (
select ca.c_address, ca.cr
from customer_address ca
where ca.c_id = c1.c_id
) loop
if c2.cr = 'N' then
update customer_master cm
set cm.c_address = (
select ca.c_address
from customer_address ca
where ca.cr = 'Y'
and ca.c_id = c1.c_id
)
where cm.c_tras = c1.c_tras
end if;
end loop;
end loop;
end;
/
Using the FOR loop allows the PL/SQL engine to handle the cursors for you - makes it much easier.
But the above is still very slow, as there are loops within loops and data is fetched for all rows, even if they are not needed.
Much better to avoid the loops within loops with a JOIN, and instead of IF statement use WHERE to only fetch the rows you actually need:
begin
for c1 in (
select cm.c_tras, cm.c_id, cm.c_add
, ca.c_address, ca.cr
from customer_master cm
join customer_address ca
on ca.c_id = cm.c_id
where ca.cr = 'N'
) loop
update customer_master cm
set cm.c_address = (
select ca.c_address
from customer_address ca
where ca.cr = 'Y'
and ca.c_id = cm.c_id
)
where cm.c_tras = c1.c_tras
end loop;
end;
/
But that is still not the best way, as @Alex Poole points out. It is even better not to do any looping at all, but instead do a single UPDATE statement that updates all the rows needed.
That could be something like this:
update customer_master cm
set cm.c_address = (
select ca.c_address
from customer_address ca
where ca.cr = 'Y'
and ca.c_id = cm.c_id
)
where cm.c_tras in (
select cm1.c_tras
from customer_master cm1
join customer_address ca
on ca.c_id = cm1.c_id
where ca.cr = 'N'
)
/
Or if the datamodel and primary keys are such that you can do a key preserved join, it perhaps could be possible to do an update on a join. (But I cannot tell if that is possible in your case - I do not know the data model ;-)
Also note, that in all cases (both your code and my rewrites) you have problems if there are multiple rows in customer_address
having cr = 'Y'
for the same c_id
. You may wish to review your datamodel and determine what you are going to if such cases arise.
回答2:
The loop is not ending because you have to invoke exit:
EXIT WHEN c1%NOTFOUND;
EXIT WHEN c2%NOTFOUND;
and remember about closing cursors:
CLOSE c1;
CLOSE c2;
I don't know the business logic so cannot help with no-action-behaviour.
来源:https://stackoverflow.com/questions/28145914/non-ending-loop-in-cursor