"选择...进入.."语句失败,"表或视图不存在",即使它不应该运行

Joh*_*tam 3 oracle plsql sqlplus

我有以下PL/SQL脚本,我试图通过SQL*Plus运行:

DECLARE 
   table_exists number;
   sequence_exists number;
   sequence_start number;
BEGIN
   select count(*) into sequence_exists
       from all_sequences
       where sequence_name='DBSEQ';
   select count(*) into table_exists
       from dba_tables
       where table_name='DBTABLE';
   IF sequence_exists = 0 AND table_exists > 0 THEN
       select MAX(ID) + 1 into sequence_start from DBTABLE;
   ELSE
       sequence_start := 1;
   END;
   IF sequence_exists = 0 THEN
       execute immediate 'CREATE SEQUENCE DBSEQ
             start with ' || sequence_start || '
             increment by 1
             nomaxvalue';
   END IF;
END;
Run Code Online (Sandbox Code Playgroud)

(这有点最小化,以显示实际失败的部分).

问题是排

select MAX(ID) + 1 into sequence_start from DBTABLE;
Run Code Online (Sandbox Code Playgroud)

失败的"PL/SQL:ORA-00942:表或视图不存在",因为该表不存在(我已经验证table_exists变量实际上是0).所以基本上,即使if子句没有执行,select运行(或至少失败).我还验证了if子句确实失败了,用例如创建一个表来替换select,这在运行脚本后不存在.

我也尝试捕获异常,做这样的事情:

BEGIN
    select MAX(ID) + 1 into sequence_start from DBTABLE;
EXCEPTION
    WHEN OTHERS THEN
        sequence_start := 1;
END;
Run Code Online (Sandbox Code Playgroud)

但那产生了同样的错误.

那么,select语句有什么特别的东西可以让它在其他任何东西之前运行吗?我该如何解决我的问题?

Jus*_*ave 6

问题是Oracle必须先编译块才能运行它.编译块的一部分涉及解析所有静态引用.如果您对不存在的表有引用,则该块将无法编译,因此无法运行.您的异常处理程序不执行任何操作,因为它是编译错误,而不是执行错误.

如果希望块引用编译块时可能不存在的表,则需要使用动态SQL.

EXECUTE IMMEDIATE 'select max(id) from dbtable'
   INTO sequence_start;
Run Code Online (Sandbox Code Playgroud)

这将允许块成功编译,因为Oracle不需要解析动态SQL语句中的引用.如果您的代码在dbtable不存在时尝试执行动态SQL语句,您将收到运行时错误(您可以使用异常处理程序捕获).