3

这听起来可能是一个愚蠢的问题,但我希望我能让自己足够清楚。

  1. 谈到Spaghetti Code时,它​​的基础是 GOTO 的使用。我有一个同行曾经说过,如果我在代码末尾放置了一个断点,并且每次都没有到达这个断点,那就是有问题
  2. 尽管如此,在 Oracle 包中使用结构是一个常见的(我会说是一个规则)EXIT WHEN (通常随后是%NOTFOUND 测试)。

理所当然地认为 usingEXIT会破坏编程流程,这不是 1 和 2 之间不匹配的东西吗?

每个人都在遵循不好的做法来使用 PL/SQL 进行编程吗?PL/SQL 是否不遵循这种特定的条件模式?

在 Oracle 的内部是否有任何性能原因使用此类语句?

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

4

2 回答 2

2

保持代码简单是非常值得推荐的,所以我可以告诉你 PL/SQL 大师对它的看法:

注意:在某些情况下不推荐使用 CURSOR-FOR-LOOP。您可以考虑一种更智能的方式,根据您的需要选择单个 SELECT-INTO 或 BULK COLLECT。

资料来源:关于光标 FOR 循环,Oracle 杂志,作者:Steven Feuerstein

(参考:Feuerstein,前二十个 PL/SQL 技巧和技术):

循环

12. 利用光标 FOR 循环。

游标 FOR 循环是我最喜欢的 PL/SQL 结构之一。它充分利用了语言的过程方面与 SQL 数据库语言的强大功能的紧密有效集成。它减少了从游标中获取数据所需编写的代码量。它大大减少了在您的编程中引入循环错误的机会 - 循环是程序中更容易出错的部分之一。这个循环听起来好得令人难以置信吗?好吧,它不是——这都是真的!

假设我需要更新住在我的宠物旅馆 Share-a-Din-Din Inn 的所有宠物的账单。下面的示例包含一个匿名块,它使用光标 occupancy_cur 来选择 Inn 中所有住客的房间号和宠物 ID 号。过程 update_bill 将任何新的更改添加到该宠物的房间费用中。

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 块转换为使用游标 FOR 循环,那么我所拥有的只是:

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 循环的美丽简洁!记录的声明一去不复返了。OPEN、FETCH 和 CLOSE 语句已经一去不复返了。不需要检查 %FOUND 属性。把一切都做好的烦恼一去不复返了。相反,您实际上对 PL/SQL 说:: Ò您和我都知道我想要每一行,并且我想将该行转储到与游标匹配的记录中。帮我处理好这个,好吗?”PL/SQL 确实会处理它,就像任何与 SQL 集成的现代编程语言一样。

于 2012-10-31T01:10:56.777 回答
2

是的,很多人都在遵循一种不好的做法。

坏作风

我同意@Osy 的观点,即 OPEN/FETCH/CLOSE 添加了完全不必要的代码。我会更进一步,说你几乎不应该使用CURSOR.

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

我坚信保持单个代码单元尽可能小。乍一看,它似乎CURSOR可以帮助您做到这一点。你可以在一个地方定义你的 SQL,然后再做 PL/SQL 循环。

但实际上,额外的间接层几乎不值得。有时很多逻辑在 SQL 中,有时很多逻辑在 PL/SQL 中。但在实践中,在两者中加入大量复杂逻辑几乎没有意义。您的代码通常最终看起来像以下之一:

for records in (<simple SQL>) loop
    <complex PL/SQL>
end loop;

或者:

for records in
(
    <complex SQL>
) loop
    <simple PL/SQL>;
end loop;

无论哪种方式,您的代码部分之一都将非常小。分离这两个代码部分的复杂性大于更大的单个代码部分的复杂性。(但这显然是我的意见。)

表现不佳

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

编译器可以在某些 for 循环中自动使用批量收集。但是,引用 Oracle 演示文稿“PL/SQL 性能——揭穿神话”,第 122 页:

不要通过使用 open、fetch 循环、close 形式放弃这个机会

这是一个简单的例子:

--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;
/
于 2012-10-31T04:37:10.487 回答