2015-01-26 2 views
0

Введите код ниже. Он работает без остановок без каких-либо операций или вывода.Non Ending Loop в курсоре.

что я делаю неправильно.

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

Как и не показано, как показано в pcej, 'cid' имеет значение null в точке, которую вы открываете' c2', и это не будет автоматически обновляться, когда вы получаете 'c1'. Ваш первый выбор 'c2' никогда не получит запись, поэтому вы сразу же выйдете. Вам нужны вложенные циклы, а не один цикл.Но вы можете сделать это с помощью одного курсора с соединением; и может использовать одно коррелированное обновление, вам вообще не нужен PL/SQL. –

ответ

0

Петля не заканчивается, потому что вы должны ссылаться на выход:

EXIT WHEN c1%NOTFOUND; 
EXIT WHEN c2%NOTFOUND; 

и помнить о закрытии курсоров:

CLOSE c1; 
CLOSE c2; 

Я не знаю, бизнес-логику, поэтому не может помочь без действий.

+0

Спасибо за быстрый ответ. Теперь петли заканчиваются. но нет никаких изменений в данных? Зачем? –

+0

Попробуйте отладить его или используйте 'dbms_output.put_line (cid || '' || cadd || '' || ctras || '' || cr || '' || cad);' for " грязная "отладка – pcejrowski

1

Как @pcej ответил, что вам не хватает некоторых частей обработки курсоров.

В целом, обычно это всего лишь несколько конкретных случаев, когда хорошая идея обрабатывать курсоры явно в вашем собственном коде. Практически во всех случаях лучше всего делать что-либо в одном SQL-заявлении, если это вообще возможно, или если на самом деле требуется цикл, то вы используете неявные курсоры FOR FOR, где PL/SQL-движок выполняет всю обработку курсора для вы.

Давайте упростить ваш шаг за шагом код, чтобы показать вам, что можно сделать ...

Во-первых, как можно избежать объявления курсоров выборки в переменные, и обработку выходов. Это можно сделать с помощью цикла FOR:

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

Использование для цикла позволяет/SQL двигатель PL для обработки курсоров для вас - делает его гораздо проще.

Но выше все еще очень медленно, так как в цикле есть петли, и данные извлекаются для всех строк, даже если они не нужны.

Гораздо лучше, чтобы избежать петли внутри петли с JOIN, и вместо IF использовать оператор WHERE только Fetch строки, которые на самом деле нужно:

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

Но это еще не самый лучший способ, как и @ Алекс Пул отмечает. Еще лучше не делать никаких циклов, но вместо этого нужно сделать одно заявление UPDATE, которое обновляет все необходимые строки.

Это может быть что-то вроде этого:

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' 
) 
/

Или если DataModel и первичные ключи таковы, что вы можете сделать ключ сохранившийся присоединиться, он, возможно, может быть возможно сделать обновление на объединение. (Но я не могу сказать, возможно ли это в вашем случае - я не знаю модель данных ;-)

Также обратите внимание, что во всех случаях (как ваш код, так и мои перезаписи) возникают проблемы, если есть несколько строк в customer_address с cr = 'Y' для того же c_id. Вы можете просмотреть свою базу данных и определить, что собираетесь, если такие случаи возникнут.