为什么此处需要立即执行



我是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
  EXECUTE IMMEDIATE 'insert into test (hello) values (''hi'')';

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

整个PL/SQL块在编译时被解析,但动态语句中的文本直到运行时才被求值。(对于匿名块,它们几乎是一样的,但步骤仍然不同)。

您的if/else也要到运行时才会求值。编译器不知道在您插入时该表是否始终存在,它只能在解析整个块时检查它是否存在。

如果表已经存在,那么它是可以的;编译器可以看到它,块执行,select得到1,然后进入else进行插入。但是,如果它不存在,那么在编译时,插入的解析将正确失败,ORA-00942,并且块中的任何内容都不会执行。

由于表的创建是动态的,所以对表的所有引用也必须是动态的-正如你所看到的那样,你的插入也是动态的,但如果你查询它的话也是如此。基本上,这会使你的代码更难阅读,并可能隐藏语法错误-因为动态代码要到运行时才能解析,而且你可能会在长时间未命中的分支中的动态语句中出错。

无论如何,全局临时表都不应该在运行中创建。它们是具有临时数据的永久对象,特定于每个会话,不应作为应用程序代码的一部分创建/删除。(您的应用程序一般不应进行架构更改;它们应仅限于升级/维护更改并受到控制,以避免错误、数据丢失和意外的副作用;GTT也不例外)。

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

创建一次GTT,并使所有PL/SQL代码成为静态代码。如果您想要更接近SQL Server的本地临时表的内容,请查看PL/SQL集合。

PL/SQL:ORA-00942:表或视图不存在

这是编译时错误,即在创建GTT之前解析静态SQL。

让我们看看编译时运行时error之间的区别:

静态SQL:

SQL> DECLARE
  2  v number;
  3  BEGIN
  4  select empno into v from a;
  5  end;
  6  /
select empno into v from a;
                         *
ERROR at line 4:
ORA-06550: line 4, column 26:
PL/SQL: ORA-00942: table or view does not exist
ORA-06550: line 4, column 1:
PL/SQL: SQL Statement ignored

动态SQL:

SQL> DECLARE
  2  v number;
  3  BEGIN
  4  execute immediate 'select empno from a' into v;
  5  end;
  6  /
DECLARE
*
ERROR at line 1:
ORA-00942: table or view does not exist
ORA-06512: at line 4

在第一个PL/SQL块中,在编译时进行了语义检查,您可以看到PL/SQL: ORA-00942: table or view does not exist。在第二个PL/SQL块中,您没有看到PL/SQL错误。

底线,

在编译时,不知道表是否存在,因为它是仅在运行时创建。

在您的情况下,为了避免这种行为,您需要使INSERT也是动态的,并使用EXECUTE IMEDIATE。通过这种方式,您可以转义编译时错误并动态创建,还可以在运行时动态插入

话虽如此,基本问题是您试图在运行中创建GTT,这不是一个好主意。你应该创建一次,然后按照你想要的方式使用它。

我对您的代码做了一点修改,就逻辑而言,它是有效的。但正如exp在早期帖子中所说,在运行时创建动态GTT根本不是一个好主意

--- Firstly by dropping the table i.e NO TABLE EXISTS in the DB in AVROY 
SET serveroutput ON;
DECLARE
  table_count INTEGER;
  var_sql     VARCHAR2(1000) := 'create GLOBAL TEMPORARY table TEST (            
hello varchar(1000) NOT NULL)';
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE AVROY.TEST'; --Added the line just to drop the table as per your comments
  SELECT COUNT(*)
  INTO table_count
  FROM all_tables
  WHERE table_name = 'TEST'
  AND OWNER        = 'AVROY';
  IF table_count   = 0 THEN
    EXECUTE IMMEDIATE var_sql;
    dbms_output.put_line('table created');
  ELSE
    INSERT INTO AVROY.test
      (hello
      ) VALUES
      ('hi'
      );
  END IF;
END;
--------------------OUTPUT-----------------------------------------------
anonymous block completed
table created
SELECT COUNT(*)
--  INTO table_count
  FROM all_tables
  WHERE table_name = 'TEST'
  AND OWNER        = 'AVROY';
COUNT(*)
------
1
--------
-- Second option is without DROPPING TABLE

SET serveroutput ON;
DECLARE
  table_count INTEGER;
  var_sql     VARCHAR2(1000) := 'create GLOBAL TEMPORARY table TEST (            
hello varchar(1000) NOT NULL)';
BEGIN
--EXECUTE IMMEDIATE 'DROP TABLE AVROY.TEST';
  SELECT COUNT(*)
  INTO table_count
  FROM all_tables
  WHERE table_name = 'TEST'
  AND OWNER        = 'AVROY';
  IF table_count   = 0 THEN
    EXECUTE IMMEDIATE var_sql;
    dbms_output.put_line('table created');
  ELSE
    INSERT INTO AVROY.test
      (hello
      ) VALUES
      ('hi'
      );
      dbms_output.put_line(SQL%ROWCOUNT||' Rows inserted into the table');
  END IF;
END;
-------------------------------OUTPUT-------------------------------------
anonymous block completed
1 Rows inserted into the table

---------------------------------------------------------------------------

相关内容

  • 没有找到相关文章

最新更新