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

时间:2013-09-04 20:16:51

标签: oracle plsql sqlplus

我有以下正在尝试通过SQL * Plus运行的PL / SQL脚本:

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;

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

问题在于行

select MAX(ID) + 1 into sequence_start from DBTABLE;

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

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

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

但是产生了同样的错误。

那么,select语句有什么特别之处,这使得它在其他任何事情之前运行吗?我该如何解决我的问题?

1 个答案:

答案 0 :(得分:6)

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

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

EXECUTE IMMEDIATE 'select max(id) from dbtable'
   INTO sequence_start;

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