4

我是 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 以及为什么我的 SELECT 语句在 BEGIN 之后仍然有效,而其他所有似乎都需要 EXECUTE IMMEDIATE 才能正常运行?

4

3 回答 3

7

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

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

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

由于表的创建是动态的,因此对表的所有引用也必须是动态的——如您所见,您的插入也是如此,如果您随后查询它。基本上,它使您的代码更难阅读并且可以隐藏语法错误 - 因为动态代码直到运行时才被解析,并且您可能在未命中的分支中的动态语句中出错很久。

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

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

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

于 2015-11-10T11:45:34.190 回答
3

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

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

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

静态 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 IMMEDIATE。这样,您可以避免编译时错误动态创建表,并在运行时动态插入

话虽如此,基本问题是您正在尝试动态创建GTT,这不是一个好主意。您应该创建一次,然后以您想要的方式使用它。

于 2015-11-10T11:45:50.207 回答
-2

我稍微修改了您的代码,就逻辑而言它可以工作。但正如前面文章中所解释的那样,在运行时动态创建 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


---------------------------------------------------------------------------
于 2015-11-10T12:07:01.923 回答