在Oracle中循环遍历游标时使用EXIT WHEN指令是一种不好的做法吗?

在Oracle中循环遍历游标时使用EXIT WHEN指令是一种不好的做法吗?,oracle,coding-style,cursor,conditional,Oracle,Coding Style,Cursor,Conditional,这听起来可能是一个愚蠢的问题,但我希望我能说清楚 当谈到它时,它的基础是 GOTOs的使用。我有一位同行曾经说过,如果我在代码末尾放了一个断点,但每次都没有到达这个断点,那么一定是出了问题 然而,这是一个常见的(我想说,是一个规则)使用 Oracle包中的结构(通常后跟%NOTFOUND 测试) 理所当然地认为使用EXIT会中断编程流程,这不是1和2之间不匹配的事情吗 每个人都在用PL/SQL编程吗?PL/SQL是否不遵循这个特定的条件模式 在甲骨文的庇护下,是否有任何性能理由使用此类语句 抱歉

这听起来可能是一个愚蠢的问题,但我希望我能说清楚

  • 当谈到它时,它的基础是 GOTOs的使用。我有一位同行曾经说过,如果我在代码末尾放了一个断点,但每次都没有到达这个断点,那么一定是出了问题
  • 然而,这是一个常见的(我想说,是一个规则)使用 Oracle包中的结构(通常后跟
    %NOTFOUND
    测试)
  • 理所当然地认为使用
    EXIT
    会中断编程流程,这不是1和2之间不匹配的事情吗

    每个人都在用PL/SQL编程吗?PL/SQL是否不遵循这个特定的条件模式

    在甲骨文的庇护下,是否有任何性能理由使用此类语句


    抱歉,如果已经问过这个问题,我找不到任何类似的问题。

    非常建议在您的代码中保持简单,这样我就可以告诉您PL/SQL专家对它的看法:

    注意:在某些情况下,不建议使用游标循环。你可以考虑一种更智能的方式,根据你的需要选择单选或批量收集。

    (参考:):

    循环

    12。利用光标进行循环。

    循环的光标为 我最喜欢的PL/SQL结构之一。它充分利用了紧密的 有效地将语言的程序方面与 SQL数据库语言的强大功能。它减少了代码量 您需要写入以从游标获取数据。这大大降低了成本 在您的编程中引入循环错误的机会-并且循环是 程序中最容易出错的部分之一。这个环路响吗 好得难以置信?好吧,不是的,都是真的

    假设我需要更新所有宠物的账单 共享丁丁酒店。下面的示例包含一个 使用光标Occupation\u cur选择房间的匿名块 酒店所有入住者的号码和宠物ID号。程序 更新账单增加了宠物房间费用的任何新变化

    DECLARE
       CURSOR occupancy_cur IS
        SELECT pet_id, room_number
        FROM occupancy
        WHERE occupied_dt = SYSDATE;
       occupancy_rec occupancy_cur%ROWTYPE;
    BEGIN
        OPEN occupancy_cur;
        LOOP
        FETCH occupancy_cur
          INTO occupancy_rec;
          EXIT WHEN occupancy_cur%NOTFOUND;
          update_bill
          (occupancy_rec.pet_id,
          occupancy_rec.room_number);
        END LOOP;
        CLOSE occupancy_cur;
    END;
    
    这段代码让人无法想象。除了定义 在游标(第2行)中,必须显式声明 光标(第5行),打开光标(第7行),启动无限循环, 从光标集中提取一行到记录中(第9行),检查 使用游标属性结束数据条件(第10行),最后 执行更新。当你做完了,你必须记住 关闭光标(第14行)。如果我将此PL/SQL块转换为使用 光标用于循环,则我所拥有的是:

    DECLARE
    CURSOR occupancy_cur IS
      SELECT pet_id, room_number
      FROM occupancy WHERE occupied_dt =
      SYSDATE;
    BEGIN
      FOR occupancy_rec IN occupancy_cur
      LOOP
        update_bill (occupancy_rec.pet_id,
        occupancy_rec.room_number);
      END LOOP;
    END;
    
    在这里,您可以看到游标FOR循环的简单性!过去了 记录的声明。打开、取出和关闭的门都不见了 声明。需要检查%FOUND属性。这些东西都不见了 担心一切都会好起来。相反,您可以对PL/SQL说 效果:Ò你和我都知道我想要每一行,我想要转储 将该行转换为与光标匹配的记录。为你照顾好那件事 我,你会吗?“PL/SQL确实会处理它,就像任何一种方式一样 与SQL集成的现代编程语言应该是


    是的,很多人都在遵循一个坏习惯

    糟糕的风格

    我同意@Osy的观点,即OPEN/FETCH/CLOSE添加了完全不必要的代码。我甚至要说,您几乎不应该使用
    游标

    首先,您通常希望尽可能多地使用纯SQL。如果您需要使用PL/SQL,请使用隐式游标。它将为您节省一行代码,并帮助您将相关逻辑保持在一起

    我非常相信保持单个代码单元尽可能小。乍一看,
    光标可以帮助您做到这一点。您可以在一个位置上定义SQL,然后稍后再执行PL/SQL循环

    但在现实中,这种额外的间接层几乎是不值得的。有时很多逻辑在SQL中,有时很多逻辑在PL/SQL中。但在实践中,在两者中都放很多复杂的逻辑是没有意义的。您的代码通常看起来像 其中一项:

    for records in (<simple SQL>) loop
        <complex PL/SQL>
    end loop;
    
    用于()循环中的记录
    端环;
    
    或:

    中的记录
    (
    )环路
    ;
    端环;
    
    无论哪种方式,您的一个代码段都会非常小。分离这两个代码段的复杂性要比单个更大代码段的复杂性大。(但这显然是我的观点。)

    性能差

    使用OPEN/FETCH/CLOSE会对性能产生重大影响。该方法比使用游标for循环或隐式游标慢得多

    编译器可以在某些for循环中自动使用大容量收集。但是,引用Oracle演示文稿第122页:

    不要使用打开、获取循环、关闭表单来浪费这个机会

    下面是一个简单的例子:

    --Sample data
    create table t(a number, b number);
    insert into t select level, level from dual connect by level <= 100000;
    commit;
    
    --OPEN/FETCH/CLOSE
    --1.5 seconds
    declare
        cursor test_cur is
        select a, b from t;
        test_rec test_cur%rowtype;
        counter number;
    begin
        open test_cur;
        loop
            fetch test_cur into test_rec;
            exit when test_cur%notfound;
            counter := counter + 1;
        end loop;
        close test_cur;
    end;
    /
    
    --Implicit cursor
    --0.2 seconds
    declare
        counter number;
    begin
        for test_rec in (select a, b from t) loop
            counter := counter + 1;
        end loop;
    end;
    /
    
    ——示例数据
    创建表t(a号、b号);
    
    插入到t select level,level from dual connect by level Hi Osi,这是一篇很棒的文章,谢谢!我喜欢在不使用
    EXIT WHEN
    条件时处理
    FOR
    循环的方法。你知道这种结构是否可以在任何地方使用吗?我的意思是,是否可以用这种
    对于
    loop?Right Tiago,即使游标有参数或您使用的是动态sql,您也可以替换所有退出选项。这两个答案都很好,只需将这一个明确标记为“更好”答案,因为它清楚地说明了两件事:这是一种不好的做法,并且还比较了这两种方法。
    --Sample data
    create table t(a number, b number);
    insert into t select level, level from dual connect by level <= 100000;
    commit;
    
    --OPEN/FETCH/CLOSE
    --1.5 seconds
    declare
        cursor test_cur is
        select a, b from t;
        test_rec test_cur%rowtype;
        counter number;
    begin
        open test_cur;
        loop
            fetch test_cur into test_rec;
            exit when test_cur%notfound;
            counter := counter + 1;
        end loop;
        close test_cur;
    end;
    /
    
    --Implicit cursor
    --0.2 seconds
    declare
        counter number;
    begin
        for test_rec in (select a, b from t) loop
            counter := counter + 1;
        end loop;
    end;
    /