我有以下正在尝试通过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语句有什么特别之处,这使得它在其他任何事情之前运行吗?我该如何解决我的问题?
答案 0 :(得分:6)
问题是Oracle必须先编译块才能运行它。编译块的一部分涉及解析所有静态引用。如果您对不存在的表有引用,则该块将无法编译,因此无法运行。您的异常处理程序不执行任何操作,因为它是编译错误,而不是执行错误。
如果希望块引用编译块时可能不存在的表,则需要使用动态SQL。
EXECUTE IMMEDIATE 'select max(id) from dbtable'
INTO sequence_start;
这将允许块成功编译,因为Oracle不需要解析动态SQL语句中的引用。如果您的代码在dbtable
不存在时尝试执行动态SQL语句,您将收到运行时错误(可以使用异常处理程序捕获)。