游标中的非结束循环。

时间:2015-01-26 07:05:13

标签: sql oracle oracle11g cursor

请找到以下代码。它不停止运行,没有操作或输出。

我做错了什么。

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

2 个答案:

答案 0 :(得分:1)

正如@pcej所说,你缺少处理游标的一些部分。

通常,通常只有少数特定情况下,在您自己的代码中明确处理游标是个好主意。在几乎所有情况下,如果可能的话,最好在单个SQL语句中执行操作,或者如果实际需要循环,则使用隐式游标FOR循环,其中PL / SQL引擎执行所有游标处理你。

让我们一步一步地简化您的代码,向您展示可以做些什么......

首先看看如何避免声明游标,获取变量以及处理EXIT。这可以通过使用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;
/

使用FOR循环允许PL / SQL引擎为您处理游标 - 使其更容易。

但上面的内容仍然很慢,因为循环中有循环,并且所有行都会获取数据,即使它们不需要也是如此。

使用JOIN避免循环内的循环要好得多,而不是IF语句使用WHERE来只获取实际需要的行:

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

但是@Alex Poole指出,这仍然不是最好的方式。最好不要进行任何循环,而是执行单个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'
 )
/

或者,如果数据模型和主键是可以执行密钥保留连接的,则可能可以对连接执行更新。 (但我无法判断你的情况是否可行 - 我不知道数据模型; - )

另请注意,在所有情况下(包括您的代码和我的重写),如果customer_address中有多个行cr = 'Y'为同一c_id,则会遇到问题。您可能希望查看您的数据模型,并确定在出现此类情况时您将要采取的措施。

答案 1 :(得分:0)

循环没有结束,因为你必须调用exit:

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

并记住关闭游标:

CLOSE c1;
CLOSE c2;

我不了解业务逻辑,因此无法帮助处理不采取行动的行为。