10

这个问题提出了一个非常有趣的观点;Oracle 文档中似乎存在关于%NOTFOUND获取后是否可能为空的矛盾。是吗?

引用11g 文档

注意:在示例 6-16 中,如果 FETCH 从不获取行,则 c1%NOTFOUND 始终为 NULL,并且永远不会退出循环。为防止无限循环,请改用此 EXIT 语句:EXIT WHEN c1%NOTFOUND OR (c1%NOTFOUND IS NULL);

该文档似乎直接自相矛盾,因为它还说了以下内容,这意味着在 fetch 之后%NOTFOUND 不能为空。

%NOTFOUND(与 %FOUND 的逻辑相反)返回:
NULL 在打开显式游标之后但在第一次获取之前
FALSE 如果从显式游标的最新获取返回一行 返回
TRUE 否则

10g 文档有一个类似的警告,这不一定是直接矛盾,因为它警告可能无法成功执行获取以显示此行为。

在第一次提取之前,%NOTFOUND 的计算结果为 NULL。如果 FETCH 从未成功执行,则 EXIT WHEN 条件永远不会为 TRUE,并且循环永远不会退出。为了安全起见,您可能希望改用以下 EXIT 语句:

当 c1%NOTFOUND 或 c1%NOTFOUND 为空时退出;

在什么情况下 fetch 可能“失败”或%NOTFOUND在 fetch 执行后可能返回 null?

4

3 回答 3

8

我可以找到获取可能失败的情况:

declare
  i integer;
  cursor c is
    select 1 / 0 from dual;
begin
  open c;

  begin
    fetch c
      into i;
  exception
    when others then
      dbms_output.put_line('ex');
  end;

  if c%notfound is null then
    dbms_output.put_line('null');
  elsif c%notfound then
    dbms_output.put_line('true');
  else
    dbms_output.put_line('false');
  end if;
  close c;

end;

但这只会使您的问题更强大,因为无论在 10g 还是在 11g 中,它都会评估为 null ...

于 2012-07-01T12:11:24.713 回答
0

我认为让你绊倒的部分是:

如果 FETCH 从未成功执行,则 EXIT WHEN 条件永远不会为 TRUE,并且循环永远不会退出。

在过去的某个地方,一定有一个看起来像这样的代码示例:

LOOP
  FETCH c1 INTO name;
  EXIT WHEN c1%NOTFOUND;
  -- Do stuff
END LOOP;

给定这段代码,那么这个陈述就成立了。如果 fetch 从未执行(失败),则 %NOTFOUND 将为空。EXIT WHEN条件不会评估为 TRUE(null 评估为 false)。然后,确实,循环将永远持续下去。

于 2012-07-01T03:57:22.357 回答
-1

这是一种很容易测试的情况:

SET SERVEROUT ON;

DECLARE
  -- this cursor returns a single row
  CURSOR c1 IS
    SELECT 1 FROM dual WHERE rownum = 1;

  -- this cursor returns no rows
  CURSOR c2 IS
    SELECT 1 FROM dual WHERE 1=0;

  v1 number;
BEGIN
  OPEN c1;
  FETCH c1 INTO v1; -- this returns a record
  FETCH c1 INTO v1; -- this does not return a record
  IF c1%NOTFOUND THEN
    dbms_output.put_line('c1%NOTFOUND: TRUE');
  ELSIF c1%NOTFOUND IS NULL THEN
    dbms_output.put_line('c1%NOTFOUND: NULL');
  ELSE
    dbms_output.put_line('c1%NOTFOUND: FALSE');
  END IF;
  CLOSE c1;

  OPEN c2;
  FETCH c2 INTO v1; -- this does not return a record
  IF c2%NOTFOUND THEN
    dbms_output.put_line('c2%NOTFOUND: TRUE');
  ELSIF c2%NOTFOUND IS NULL THEN
    dbms_output.put_line('c2%NOTFOUND: NULL');
  ELSE
    dbms_output.put_line('c2%NOTFOUND: FALSE');
  END IF;
  CLOSE c2;
END;
/

Oracle APEX 4.1 上的脚本输出是(我认为 APEX 运行的是 Oracle 11gR2,但您可以轻松地在任何版本上运行该脚本):

c1%NOTFOUND: TRUE
c2%NOTFOUND: TRUE

根据此测试,%NOTFOUND执行 fetch 后不会为 NULL。这与 10g 和 11g 文档在%NOTFOUND属性的初始描述中所说的相匹配。关于循环永不退出的注释必须来自该示例的旧版本。由于它只是一个注释,我会说信任最初的描述并忽略该注释是安全的。

于 2012-07-01T06:13:52.297 回答