我是SQL Server用户,并且有一个使用Oracle的小项目,因此,我试图了解Oracle的某些特殊性,我认为需要一些帮助以更好地了解以下情况:

我想在创建临时表之前测试它是否存在,所以在这里有以下代码:

DECLARE
  table_count INTEGER;
  var_sql VARCHAR2(1000) := 'create GLOBAL TEMPORARY table TEST (
            hello varchar(1000) NOT NULL)';
BEGIN
  SELECT COUNT(*) INTO table_count FROM all_tables WHERE table_name = 'TEST';

  IF table_count = 0 THEN
    EXECUTE IMMEDIATE var_sql;
  END IF;
END;


它正常工作,因此在执行一次之后,在我的IF上添加了else语句:

ELSE
  insert into test (hello) values ('hi');


再次执行它,并将一行添加到我的测试表中。

好的,我的代码已经准备就绪并且可以正常工作,因此我删除了临时表并尝试再次运行整个语句,但是当我这样做时,出现以下错误:

ORA-06550: line 11, column 19:
PL/SQL: ORA-00942: table or view does not exist
ORA-06550: line 11, column 7:
PL/SQL: SQL Statement ignored
06550. 00000 -  "line %s, column %s:\n%s"
*Cause:    Usually a PL/SQL compilation error.
*Action:


然后,我将else语句更改为此,现在它又可以工作了:

ELSE
  EXECUTE IMMEDIATE 'insert into test (hello) values (''hi'')';


我的问题是,为什么单独运行我可以简单地使用插入而不是EXECUTE IMMEDIATE,为什么在其余所有似乎都需要EXECUTE IMMEDIATE才能正常运行时,为什么在BEGIN之后我的SELECT语句仍然可以工作?

最佳答案

整个PL / SQL块在编译时进行解析,但是直到运行时才评估动态语句中的文本。 (对于匿名块,它们接近同一件事,但是仍然是不同的步骤)。

您的if / else直到运行时也不会评估。编译器并不知道该表在您插入时将一直存在,它只能检查在分析整个块时该表是否存在。

如果该表已经存在,那就可以了。编译器可以看到它,该块执行,您的选择为1,然后进入else进行插入。但是,如果它不存在,则插入的解析将在编译时通过ORA-00942正确地失败,并且该块中的任何内容均不会执行。

由于表的创建是动态的,因此对该表的所有引用也必须是动态的-如您所见,您的插入内容也包括查询内容。基本上,这会使您的代码更难阅读,并且可以隐藏语法错误-因为动态代码要等到运行时才能解析,并且有可能在分支中的动态语句中出错,而该语句不会被很久。

无论如何,都不应该即时创建全局temporary tables。它们是具有临时数据的永久对象,特定于每个会话,并且不应作为应用程序代码的一部分进行创建/删除。 (您的应用程序一般不应进行任何模式更改;应该将它们限制在升级/维护更改之内,并加以控制,以避免错误,数据丢失和意外的副作用; GTT不变)。


与其他一些关系数据库中的临时表不同,在Oracle数据库中创建临时表时,将创建静态表定义。临时表是数据字典中描述的持久对象,但是在您的会话将数据插入表中之前显示为空。您为数据库本身而不是为每个PL / SQL存储过程创建一个临时表。


一次创建GTT,并使所有PL / SQL代码变为静态。如果要更接近SQL Server的本地临时表,请查看PL/SQL collections

08-25 10:23
查看更多