3

我尝试通过 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 子句不执行,选择也会运行(或至少失败)。我还验证了 if 子句确实失败了,方法是将 select 替换为创建一个表,该表在运行脚本后不存在。

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

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

但这产生了同样的错误。

那么,select 语句有什么特别之处,使它先于其他任何东西运行吗?我应该如何解决我的问题?

4

1 回答 1

7

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

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

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

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

于 2013-09-04T20:21:06.617 回答