15

我知道获取游标将使我能够访问 %ROWCOUNT、%ROWTYPE、%FOUND、%NOTFOUND、%ISOPEN 等变量

...但我想知道是否还有其他使用理由

打开 - 获取 - 关闭指令以循环游标

而不是

用 FOR 循环循环光标...(在我看来,这更好,因为它很简单)

你怎么看?

4

4 回答 4

27

从性能的角度来看,这种差异比OMG Ponies所暗示的 Tim Hall 提示要复杂得多。我相信这个技巧是对已经为网络摘录的更大部分的介绍——我希望蒂姆继续在书中提出大部分(如果不是全部)这些观点。此外,整个讨论取决于您使用的 Oracle 版本。我相信这对于 10.2、11.1 和 11.2 是正确的,但如果您开始回到旧版本,肯定会有差异。

首先,提示中的特定示例是相当不切实际的。我从未见过有人使用显式游标而不是 SELECT INTO 编写单行提取代码。因此,SELECT INTO 更有效的事实在实际中的重要性非常有限。如果我们讨论循环,我们感兴趣的性能是获取许多行的成本。这就是复杂性开始出现的地方。

Oracle 在 10.1 中引入了将游标中的数据批量收集到 PL/SQL 集合中的能力。这是将数据从 SQL 引擎获取到 PL/SQL 集合的一种更有效的方法,因为它允许您通过一次获取许多行来最小化上下文转换。并且对这些集合的后续操作更加高效,因为您的代码可以保留在 PL/SQL 引擎中。

但是,为了最大限度地利用 BULK COLLECT 语法,您通常必须使用显式游标,因为这样您可以填充 PL/SQL 集合,然后使用 FORALL 语法将数据写回数据库(在合理的假设是,如果您在游标中获取一堆数据,则很有可能您正在执行某种操作并将被操作的数据保存在某处)。如果您在 FOR 循环中使用隐式游标,正如 OMG Ponies 正确指出的那样,Oracle 将在后台执行 BULK COLLECT 以降低获取数据的成本。但是您的代码将执行较慢的逐行插入和更新,因为数据不在集合中。

一般来说,假设您使用的是 10.2 或更高版本,并且您的代码正在获取数据并将其写回数据库,

最快的

  1. 显式游标对本地集合执行 BULK COLLECT(具有适当的 LIMIT)并使用 FORALL 写回数据库。
  2. 隐式游标在幕后为您执行 BULK COLLECT 以及将单行写回数据库。
  3. 不执行 BULK COLLECT 且不利用 PL/SQL 集合的显式游标。

最慢的

另一方面,使用隐式游标可以让您在重构旧代码或学习新功能时以极少的前期成本获得大量使用批量操作的好处。如果您的大部分 PL/SQL 开发是由主要语言是其他语言或不一定跟上新语言特性的开发人员完成的,那么 FOR 循环将比使用所有新的批量收集功能。而当 Oracle 将来引入新的优化时,隐式游标代码更有可能自动获得好处,而显式代码可能需要一些手动修改。

当然,当您对性能进行故障排除到您真正关心循环代码的不同变体可能有多快时,您通常会考虑将更多逻辑移动到纯 SQL 中并完全放弃循环代码。

于 2010-10-07T21:07:23.970 回答
7

OPEN / FETCH / CLOSE 称为显式游标语法;后者称为隐式游标语法。

您已经注意到的一个关键区别是,您不能在隐式游标中使用 %FOUND/%NOTFOUND/etc... 要注意的另一件事是隐式游标比显式游标更快——它们提前读取(~ 100 条记录?)除了不支持显式逻辑。

附加信息:

于 2010-10-07T20:31:20.967 回答
3

除了一个之外,我不知道这两种实现中的任何关键区别:for ... loop在循环完成后隐式关闭光标,如果open ... fetch ... close语法你宁愿自己关闭光标(只是一种好的方式) - 认为这不是必需的: Oracle 将关闭游标自动超出可见范围。你也不能在游标中使用%FOUND和。%NOTFOUNDfor ... loop

至于我,我发现for ... loop实现更容易阅读和支持。

于 2010-10-07T20:39:28.053 回答
3

如果我错了,请纠正我,但我认为两者都有一个很好的功能,而另一个没有。

使用 for 循环,您可以这样做:

for i in (select * from dual)
  dbms_output.put_line('ffffuuu');
end loop;

使用 open .. fetch 你可以这样做:

declare
  cur sys_refcursor;
  tmp dual.dummy%type;
begin
  open cur for 'select dummy from dual';
  loop
    fetch cur into tmp;
    exit when cur%notfound;
    dbms_output.put_line('ffffuuu');
  end loop;
  close cur;
end;

因此,使用 open fetch 您可以使用动态游标,但使用 for 循环您可以在不声明的情况下定义普通游标。

于 2010-10-08T17:47:28.867 回答