TOO_MANY_ROWS 引发,但变量仍然获得一个值

And*_*Dan 5 oracle plsql oracle11g

我刚刚发现,如果您有一个引发 TOO_MANY_ROWS 异常的 SELECT INTO,该变量仍会从查询检索到的第一条记录中分配值。这是预期的行为吗?

这是我的例子:

for co in my_cursor loop
        l_sco_db_id := null;

    begin
      select db_id
        into l_sco_db_id
        from objects_tab
       where object_name  = co.object_name;

    exception
      when no_data_found then
        dbms_output.put_line('No objects_tab record found for Object ' || co.object_name);
      when too_many_rows then
        dbms_output.put_line('Multiple objects_tab records found for Object ' || co.object_name);
        l_sco_db_id := null;
    end;
end loop;
Run Code Online (Sandbox Code Playgroud)

这是在一个循环内,所以我在开始时将变量设置为 null 以确保它是空白的,但是我不得不在 WHEN TOO_MANY_ROWS 异常中再次明确地这样做,这是我没想到的。我的同事(至少,那些直接听到的)也没有期望这个变量有一个值。

Jus*_*ave 5

这是预期的行为,因为当你了解幕后发生的事情时,它是有道理的。但当你第一次看到它时,这绝对是一种看起来很奇怪的行为。从技术上讲,该行为被记录为未定义,因此不应依赖它,并且将来可能会发生变化。

在幕后, aselect into只是语法糖

  • 打开光标
  • 从游标中取出一行到目标变量中
  • no_data_found如果没有获取行则抛出异常
  • 尝试从游标中获取第二行,too_many_rows如果第二次获取成功则抛出异常。

鉴于此,目标变量将在第一次获取时写入是有道理的。但是,该select into语句的 Oracle 文档指出

PL/SQL 引发预定义的异常 TOO_MANY_ROWS 并且 INTO 子句中的变量值未定义。

因此,Oracle 可以自由地保持该值不变,或者让变量具有获取的第一行或第二行的值,或者实际上是其他任何值。并且您不应该编写依赖于任何特定行为的代码。

例如,如果您查看Jeff Kemp 的这篇博文,该变量会采用获取的第一行中的值。但是如果你对 Jeff 的代码做一个小的调整,这样你就可以获取一个局部变量

CREATE or replace PROCEDURE proc2 (v OUT NUMBER) IS
  l_v integer;
BEGIN
   SELECT 1 INTO l_v FROM all_objects;
EXCEPTION
   WHEN TOO_MANY_ROWS THEN
      dbms_output.put_line
         ('TOO MANY ROWS: v='
          || l_v);
      v := l_v;
END;
/
Run Code Online (Sandbox Code Playgroud)

然后行为会发生变化并且该值似乎不会被覆盖。

DECLARE
   v NUMBER;
BEGIN
   proc2(v);
   dbms_output.put_line('AFTER: v=' || v);
END;
/
Run Code Online (Sandbox Code Playgroud)