【问题标题】:Can %NOTFOUND return null after a fetch?提取后 %NOTFOUND 可以返回 null 吗?
【发布时间】:2012-07-01 15:47:31
【问题描述】:

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

引用11g documentation

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

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

%NOTFOUND(%FOUND 的逻辑反义词)返回:
在显式游标打开之后但在第一次提取之前为 NULL
如果从显式游标的最新提取返回一行,则为 FALSE
否则为真

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

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

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

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

【问题讨论】:

    标签: oracle plsql database-cursor


    【解决方案1】:

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

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

    过去一定有一个代码示例,看起来像这样:

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

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

    【讨论】:

    • 为什么 fetch 在那种情况下永远不会执行?
    • 查询可能存在问题,它在语法上是有效的,但由于其他因素而失败。
    • 问题的重点是什么还有那些其他因素:-)?我想不出任何一种方法可以让代码通过 fetch 以评估 %notfound 但 fetch 不会“工作”。
    • 当谈到糟糕的例子和措辞时,我和文档一样内疚。即使有错误,提取也应该导致 %notfound 为真或假。在第一次获取之前它只是 null (如文档前面所述)。因此,如果 FETCH 从未执行(成功或其他方式),则 %notfound 为空。
    【解决方案2】:

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

    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
    

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

    【讨论】:

      【解决方案3】:

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

      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 ...

      【讨论】:

      • +1 因为过于聪明:-)。文档中的示例没有嵌套匿名块...
      猜你喜欢
      • 2012-07-06
      • 2016-10-17
      • 2010-11-10
      • 2023-03-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多