你能从立即执行语句中退出 PL-SQL 循环吗?

Can you exit a PL-SQL loop from within an execute immediate statement?

我有以下代码片段(经过简化以排除无关的细节):

<<cursor_loop>> 
LOOP 
  fetch c1 into somerecord;
  EXECUTE IMMEDIATE 'begin EXIT cursor_loop WHEN 1 = 1; end;';
END LOOP cursor_loop;

当我 运行 这样做时,它失败并出现 PLS-00201: identifier 'CURSOR_LOOP' must be declared 错误。

如果我将循环标签留在立即执行之外,我会得到 PLS-00376: illegal EXIT/CONTINUE statement; it must appear inside a loop

显然后者是错误的,但不清楚前者为何如此。

这个循环可以从立即执行的语句中退出吗?

我认为不是。 EXECUTE IMMEDIATE 用于 运行 一个 SQL 语句,而不是匿名块。来自文档:

The EXECUTE IMMEDIATE statement builds and executes a dynamic SQL
statement in a single operation. It is the means by which native dynamic SQL processes most dynamic SQL statements.

https://docs.oracle.com/cd/B28359_01/appdev.111/b28370/executeimmediate_statement.htm

不,你不能。

动态 SQL 语句在单独的范围内运行——它不能引用调用块中定义的变量或操纵它们的值(当然,除非你的动态语句具有允许你使用 EXECUTE IMMEDIATEUSINGINTO 子句显式创建两者之间的接口以传入和 return 值)。同样,它不能引用循环名称,因为在执行动态语句时该名称不在范围内。

在这种情况下,不清楚为什么您首先要使用 EXECUTE IMMEDIATE 而不是将 EXIT 编码为循环的正常部分。

由于范围的原因,您不能直接在动态 SQL 中引用 cursor_loop,正如其他人已经说过的那样。如果您坚持使用这种模式,那么您可以使用绑定变量标志将状态信息从动态代码传递回静态代码;类似于:

DECLARE
  break_loop pls_integer;
...
  break_loop := 0;
  <<cursor_loop>> 
  LOOP 
    fetch c1 into somerecord;
    EXECUTE IMMEDIATE 'begin if 1 = 1 then :break_loop := 1; end if; end;'
      USING OUT break_loop;
    EXIT cursor_loop WHEN break_loop = 1;
  END LOOP cursor_loop;
...

稍微更完整,虽然显然仍然非常做作,例如:

DECLARE
  break_loop pls_integer;
  somevar number;
  c1 sys_refcursor;
BEGIN
  OPEN c1 FOR
    select 1 from dual
    union all select 2 from dual
    union all select 3 from dual;

  break_loop := 0;
  dbms_output.put_line('before loop, break_loop is ' || break_loop);
  <<cursor_loop>> 
  LOOP 
    fetch c1 into somevar;
    exit when c1%notfound;
    dbms_output.put_line('got ' || somevar);
    EXECUTE IMMEDIATE 'begin if :somevar = 2 then :break_loop := 1; end if; end;'
      USING somevar, OUT break_loop;
    EXIT cursor_loop WHEN break_loop = 1;
  END LOOP cursor_loop;
  dbms_output.put_line('after loop, break_loop is ' || break_loop);
END;
/

PL/SQL procedure successfully completed.

before loop, break_loop is 0
got 1
got 2
after loop, break_loop is 1

在获取值“3”之前,由于动态检查退出了循环。