4

I am a SQL Server user and I have a small project to do using Oracle, so I’m trying to understand some of the particularities of Oracle and I reckon that I need some help to better understand the following situation:

I want to test if a temporary table exists before creating it so I had this code here:

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;

It works normally, so after I executed it once, I added an else statement on my IF:

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

Executed it again and a line was added to my test table.

Ok, my code was ready and working, so I dropped the temp table and tried to run the entire statement again, however when I do that I get the following error:

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:

Then I changed my else statement to this and now it works again:

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

My question is why running individually I can simply use the insert instead of the EXECUTE IMMEDIATE and also why my SELECT statement right after BEGIN still works when all the rest appears to need EXECUTE IMMEDIATE to run properly?

Lalit Kumar B
  • 43,345
  • 10
  • 82
  • 112
Rafael Merlin
  • 1,997
  • 19
  • 29
  • 5
    Possible duplicate of [this question](http://stackoverflow.com/q/26212557/266304) and others; the static SQL is parsed at compilation time, before the table creation is executed. But don't create the GTT on the fly, it should be a permanent object... This is an area where you shouldn't transfer direct from SQL Server to Oracle. See also [this](http://stackoverflow.com/a/8914523/266304), and [this](http://stackoverflow.com/a/27695462/266304), and [this](http://stackoverflow.com/a/3682502/266304). – Alex Poole Nov 10 '15 at 11:32

3 Answers3

7

The whole PL/SQL block is parsed at compile time, but the text within a dynamic statement isn't evaluated until runtime. (They're close to the same thing for an anonymous block, but still distinct steps).

Your if/else isn't evaluated until runtime either. The compiler doesn't know that the table will always exist by the time you do your insert, it can only check whether or not it exists at the point it parses the whole block.

If the table does already exist then it's OK; the compiler can see it, the block executes, your select gets 1, and you go into the else to do the insert. But if it does not exist then the parsing of the insert correctly fails with ORA-00942 at compile time and nothing in the block is executed.

Since the table creation is dynamic, all references to the table have to be dynamic too - your insert as you've seen, but also if you then query it. Basically it makes your code much harder to read and can hide syntax errors - since the dynamic code isn't parsed until run-time, and it's possible you could have a mistake in a dynamic statement in a branch that isn't hit for a long time.

Global temporary tables should not be created on-the-fly anyway. They are permanent objects with temporary data, specific to each session, and should not be created/dropped as part of your application code. (No schema changes should be made by your application generally; they should be confined to upgrade/maintenance changes and be controlled, to avoid errors, data loss and unexpected side effects; GTTs are no different).

Unlike temporary tables in some other relational databases, when you create a temporary table in an Oracle database, you create a static table definition. The temporary table is a persistent object described in the data dictionary, but appears empty until your session inserts data into the table. You create a temporary table for the database itself, not for every PL/SQL stored procedure.

Create the GTT once and make all your PL/SQL code static. If you want something closer to SQL Server's local temporary tables then look into PL/SQL collections.

Alex Poole
  • 161,851
  • 8
  • 150
  • 257
  • Thanks a lot. I was able to understand how the compiler parses the queries a bit better. About the GTT, I don't intend to use them this way. But since I'm starting go get to know the language so I wanted to understand why I needed to use the Immediate at this time. Just out of curiosity I did this using real tables and the behavior was exactly the same. Thanks for the GTT warning though. – Rafael Merlin Nov 10 '15 at 12:05
  • @RafaelMerlin - yes, the behaviour is nothing to do with it being a GTT rather than a 'normal' table. Creating any schema objects at runtime is usually going to cause you problems and should be avoided. Also, the `select` works because the `all_tables` view does exist at compile time. (But you should use `user_tables`, or specify the owner, as AvrajitRoy suggested). – Alex Poole Nov 10 '15 at 12:08
3

PL/SQL: ORA-00942: table or view does not exist

It is compile time error, i.e. when the static SQL is parsed before even the GTT is created.

Let's see the difference between compile time and run time error:

Static 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

Dynamic 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

In the 1st PL/SQL block, there was a semantic check at compile time, and you could see the PL/SQL: ORA-00942: table or view does not exist. In the 2nd PL/SQL block, you do not see the PL/SQL error.

Bottomline,

At compile time it is not known if the table exists, as it is only created at run time.

In your case, to avoid this behaviour, you need to make the INSERT also dynamic and use EXECUTE IMMEDIATE. In that way, you can escape the compile time error and get the table created dynamically and also do an insert into it dynamically at run time.

Having said that, the basic problem is that you are trying to create GTT on the fly which is not a good idea. You should create it once, and use it the way you want.

Lalit Kumar B
  • 43,345
  • 10
  • 82
  • 112
-2

I have modified your code a litle bit and it works as far as logic is concerned. But as exp[lained in earlier posts creating GTT on the fly at run time is not at all is a goood idea.

--- 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


---------------------------------------------------------------------------
Chand Priyankara
  • 6,485
  • 1
  • 38
  • 59
Avrajit Roy
  • 2,977
  • 1
  • 9
  • 22
  • 2
    This will still error if the table does not exist when the block is run. Adding the schema name doesn't change that. – Alex Poole Nov 10 '15 at 12:09
  • @Alex: If the table doesn't exists then the logic will go to GTT creating GTT and create it on the FLY. Secondly if exists then it will go the INSERT Statement. Adding Owner name is generally a good practice so that it should not fail in any case. – Avrajit Roy Nov 10 '15 at 12:13
  • 1
    If the table doesn't exist then the *compilation* will fail and nothing will be executed, it won't get to the dynamic creation. That's the point of the question, and why the OP gets ORA-00942 when the table doesn't exist. Your version will get the same error. (Specifying `owner` in the `select` is a good point though). – Alex Poole Nov 10 '15 at 12:15
  • Hi Alex i have modified the edit and it works in all the cases as explained. Please check both the conditions i have specified. – Avrajit Roy Nov 10 '15 at 12:23
  • You ran both of those while the table already existed. **Drop the table first** (statically, outside the block), then try to run either version. You'll still get a compile-time ORA-00942 from the static `insert` from both of them. As shown in the question. – Alex Poole Nov 10 '15 at 12:24
  • I ran both the versions are working. I have posted with output in the post – Avrajit Roy Nov 10 '15 at 12:25
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/94705/discussion-between-avrajit-roy-and-alex-poole). – Avrajit Roy Nov 10 '15 at 12:29