ORA-00942 error when referencing table after EXECUTE IMMEDIATE create table - oracle

I try to run the following snippet in PL/SQL Developer, but the last command throws an ORA-00942: table or view does not exist error message.
DECLARE
sqlCommandABC varchar2(30000) := 'create table ABC_TMP
tablespace &TBS_NORMAL_TABLES initrans 32 AS (SELECT ABC_ID from ABC where ID <=400000)';
BEGIN
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE ABC_TMP';
EXCEPTION
WHEN OTHERS THEN NULL;
END;
EXECUTE IMMEDIATE sqlCommandABC;
COMMIT;
END;
/
DECLARE
sqlCommandDEF varchar2(30000) := 'create table DEF_TMP
tablespace &TBS_NORMAL_TABLES initrans 32 AS (SELECT DEF_ID from DEF where ID <=15000)';
BEGIN
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE DEF_TMP';
EXCEPTION
WHEN OTHERS THEN NULL;
END;
EXECUTE IMMEDIATE sqlCommandDEF;
COMMIT;
END;
/
DECLARE
sqlCommandXYZ varchar2(30000) := 'create table XYZ_TMP
tablespace &TBS_NORMAL_TABLES initrans 32 AS (select ID from XYZ where ABC_1 in (SELECT ABC_ID from ABC_TMP) or DEF_1 in (SELECT DEF_ID from DEF_TMP))';
BEGIN
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE XYZ_TMP';
EXCEPTION
WHEN OTHERS THEN NULL;
END;
EXECUTE IMMEDIATE sqlCommandXYZ;
COMMIT;
END;
/
I want to create two temporary tables by selecting data from the original table, with some condition according to the logic.
After the ABC_TMP and the DEF_TMP created, I need to use them in a condition for the select of the third temporary table.
The XYZ_TMP table has two conditions, one for the ABC_TMP records and another for the DEF_TMP records.
This third create table clause throws the error.
I know i could use the original selects here, but those are pretty complex and has a relatively high cost, so if it is possible i want to use the filtered tables. I also know that if I open a new session after the first two tables created it will work, but I want to execute these in one script.

according to the provided info, I understood that DEF_TMP and ABC_TMP is successfully created and thus available. in that case, there are two possibilities here.
First is XYZ table is not exists
Second is the user trying to execute this command does not have select grant.

Related

Drop a Temp Table If Exists In Oracle

I am a new user to Oracle but have advanced knowledge of SQL Server. When working with temp tables in SQL Server we would always check to see if the table existed and drop it if it did before creating a new one. This would allow for the SQL script to be executed over and over without an error.
IF OBJECT_ID(N'tempdb..#CnsmrHstRwNm', N'U') IS NOT NULL
DROP TABLE #CnsmrHstRwNm;
CREATE TABLE #CnsmrHstRwNm;
I cannot seem to be able to do the same in Oracle. I have researched several ideas, including one who said it was a bad idea, but none of them have worked. Here is one scenario that I have tried. Does someone have any ideas?
I've referenced Oracle: If Table Exists and it seems like this could work, but I cannot figure out how to create the table after dropping it.
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE ' || 'MY_TEMP_TABLE';
EXCEPTION
WHEN OTHERS THEN NULL;
CREATE GLOBAL TEMPORARY TABLE MY_TEMP_TABLE (a varchar2(1), b varchar2(1));
END;
Can anyone help?
I'd like to second anyone who said that - in Oracle - we don't drop/create tables within PL/SQL. That's just a bad idea. Create table once, use it as many times as you want. Delete (or truncate) its contents, insert/update rows, but don't (re)create it over and over again.
Anyway, here's how; I included message so that you could follow what's going on.
At first, table doesn't exist:
SQL> desc test
ERROR:
ORA-04043: object test does not exist
Run the script:
SQL> set serveroutput on
SQL> begin
2 begin
3 dbms_output.put_line('Trying to drop a table');
4 execute immediate 'drop table test';
5 dbms_output.put_line('Table dropped');
6 exception
7 when others then
8 dbms_output.put_line('Error when dropping the table: ' || sqlerrm);
9 null;
10 end;
11 dbms_output.put_line('Creating a table');
12 execute immediate 'create table test (a varchar2(1))';
13 dbms_output.put_line('Table created');
14 end;
15 /
Trying to drop a table
Error when dropping the table: ORA-00942: table or view does not exist
Creating a table
Table created
PL/SQL procedure successfully completed.
SQL> desc test
Name Null? Type
----------------------------------------- -------- ----------------------------
A VARCHAR2(1)
Right; table now exists. You'd use it and - later - run the script once again:
SQL> begin
2 begin
3 dbms_output.put_line('Trying to drop a table');
4 execute immediate 'drop table test';
5 dbms_output.put_line('Table dropped');
6 exception
7 when others then
8 dbms_output.put_line('Error when dropping the table: ' || sqlerrm);
9 null;
10 end;
11 dbms_output.put_line('Creating a table');
12 execute immediate 'create table test (a varchar2(1))';
13 dbms_output.put_line('Table created');
14 end;
15 /
Trying to drop a table
Table dropped
Creating a table
Table created
PL/SQL procedure successfully completed.
SQL>
Rather than using a GLOBAL TEMPORARY TABLE, if you use a PRIVATE TEMPORARY TABLE then it automatically drops when the transaction is completed:
CREATE PRIVATE TEMPORARY TABLE ora$ptt_temp (
a varchar2(1), b varchar2(1)
);
Then when you COMMIT the table is automatically dropped (and you can recreate it in the next transaction without having to drop and recreate it).
However, if you do want a permanent table then use EXECUTE IMMEDIATE to drop and create it and catch (and ignore) the exception if the table does not exist:
DECLARE
does_not_exist EXCEPTION;
PRAGMA EXCEPTION_INIT(does_not_exist, -942);
BEGIN
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE MY_TEMP_TABLE';
EXCEPTION
WHEN does_not_exist THEN
NULL;
END;
EXECUTE IMMEDIATE 'CREATE GLOBAL TEMPORARY TABLE MY_TEMP_TABLE (a varchar2(1), b varchar2(1))';
END;
/

Table exists in stored procedure while used in select but not when Used in Insert statement

My stored procedure is like this:
create or replace procedure tpk.sp_Test_proc
IS
err_code NUMBER;
err_msg VARCHAR (500);
v_tbl_cnt NUMBER;
v_tbl_valid NUMBER;
Begin
SELECT COUNT(*) INTO v_tbl_cnt FROM USER_TABLES
WHERE TABLE_NAME IN (UPPER('Tbl1'),UPPER('tbl2'),UPPER('tbl3'));
IF(v_tbl_cnt =3) THEN
EXECUTE IMMEDIATE 'TRUNCATE TABLE Tbl1';
EXECUTE IMMEDIATE 'TRUNCATE TABLE Tbl2';
EXECUTE IMMEDIATE 'TRUNCATE TABLE Tbl3';
EXECUTE IMMEDIATE 'DROP TABLE Tbl1';
EXECUTE IMMEDIATE 'DROP TABLE Tbl2';
EXECUTE IMMEDIATE 'DROP TABLE Tbl3';
EXECUTE IMMEDIATE
'CREATE global temporary TABLE tbl1
( Id Integer... )'
Insert into tbl1
Select * from another_schema.Dw_table /* In this line it throws error Table does not exist */
end if;
end;
I tired same table with store procedure only to fetch the data its working there but when I used in Insert statement it throws an error
PL/SQL: ORA-00942 table or view does not exist.
I am totally confused - what's wrong here?
Select * from another_schema.Dw_table
You don't have a privilege to select from that table. Even if you think you do (granted via a role), it won't work in stored procedures - you have to grant it directly to user you're connected to.
Besides, there's no point in truncating tables first, and dropping them next. Just drop them.
Furthermore, there's rarely need to create tables dynamically (the way you do it), especially global temporary tables. Create them once, use them many times. No dropping. No (re)creating them in PL/SQL.

Can I directly define a trigger in all_triggers table on a table?

I am performing an archival process on a huge database and it involves deleting the production active table and renaming another table to be the new production table. When dropping the production active table, the triggers also get deleted. So I am just taking a backup of the triggers defined on my table using
select * from all_triggers where table_name=mytablename;
My question is, can I directly copy these triggers in to the all_triggers table after I rename my other table to be the new production active table? Will the triggers still work?
Same question for defining indexes and constraints too.
Copying the triggers from one table to another can be done by copying DDL, and not updating all_triggers table. This can be done by using DBMS_METADATA.
The closest practical example I found here: Copy Triggers when you Copy a Table
The following script can be amended as per your need:
declare
p_src_tbl varchar2(30):= 'PERSONS'; --your table name
p_trg_tbl varchar2(30):= 'PSN2'; --your trigger name
l_ddl varchar2(32000);
begin
execute immediate 'create table '||p_trg_tbl||' as select * from '||p_src_tbl||' where 1=2';
for trg in (select trigger_name from user_triggers where table_name = p_src_tbl) loop
l_ddl:= cast(replace(replace(dbms_metadata.get_ddl( 'TRIGGER', trg.trigger_name),p_src_tbl,p_trg_tbl),trg.trigger_name,substr(p_trg_tbl||trg.trigger_name, 1, 30)) as varchar2);
execute immediate substr(l_ddl, 1, instr(l_ddl,'ALTER TRIGGER')-1);
end loop;
end;
/
No, you cannot directly manipulate data dictionary tables. You can't insert data directly into all_triggers (the same goes for any data dictionary table). I guess you probably could given enough hacking. It just wouldn't work and would render your database unsupported.
The correct way to go is to script out your triggers and reapply them later. If you want to do this programmatically, you can use the dbms_metadata package. If you want to get the DDL for each of the triggers on a table, you can do something like
select dbms_metadata.get_ddl( 'TRIGGER', t.trigger_name, t.owner )
from all_triggers t
where table_owner = <<owner of table>>
and table_name = <<name of table>>
To replicate your scenario i have prepared below snippet. Let me know if this helps.
--Simple example to copy Trigger from one table to another
CREATE TABLE EMP_V1 AS
SELECT * FROM EMP;
--Creating Trigger on Old Table for Example purpose
CREATE OR REPLACE TRIGGER EMP_OLD_TRIGGER
AFTER INSERT OR UPDATE ON EMP FOR EACH ROW
DECLARE
LV_ERR_CODE_OUT NUMBER;
LV_ERR_MSG_OUT VARCHAR2(2000);
BEGIN
dbms_output.put_line('Your code for data Manipulations');
--Like Insert update or DELETE activities
END;
-- To replace this trigger for emp_v2 table
set serveroutput on;
DECLARE
lv_var LONG;
BEGIN
FOR i IN (
SELECT OWNER,TRIGGER_NAME,DBMS_METADATA.GET_DDL('TRIGGER','EMP_OLD_TRIGGER') ddl_script FROM all_triggers
WHERE OWNER = 'AVROY') LOOP
NULL;
lv_var:=REPLACE(i.ddl_script,'ON EMP FOR EACH ROW','ON EMP_V1 FOR EACH ROW');
dbms_output.put_line(substr(lv_var,1,INSTR(lv_var,'ALTER TRIGGER',1)-1));
EXECUTE IMMEDIATE 'DROP TRIGGER '||I.TRIGGER_NAME;
EXECUTE IMMEDIATE lv_var;
END LOOP;
END;
--Check if DDL manipulation has been done for not
SELECT OWNER,TRIGGER_NAME,DBMS_METADATA.GET_DDL('TRIGGER','EMP_OLD_TRIGGER') ddl_script FROM all_triggers
WHERE OWNER = 'AVROY';
---------------------------------OUTPUT----------------------------------------
"
CREATE OR REPLACE TRIGGER "AVROY"."EMP_OLD_TRIGGER"
AFTER INSERT OR UPDATE ON EMP_V1 FOR EACH ROW
DECLARE
LV_ERR_CODE_OUT NUMBER;
LV_ERR_MSG_OUT VARCHAR2(2000);
BEGIN
dbms_output.put_line('Your code for data Manipulations');
--Like Insert update or DELETE activities
END;
"
-----------------------------OUTPUT----------------------------------------------

Oracle Execute Immediate with DDL and Nested table

I have a problem trying to use an Execute Immediate statement containing a CREATE TABLE statement and a user defined Table Type. I get error ORA-22905 on Oracle 11g.
Is there any workaround to solve this issue?
CREATE TYPE MY_TABLE_TYPE AS TABLE OF VARCHAR2(30);
/
DECLARE
MT MY_TABLE_TYPE;
BEGIN
SELECT * BULK COLLECT INTO MT FROM DUAL;
-- Two steps
EXECUTE IMMEDIATE 'CREATE TABLE MY_TABLE1 (A VARCHAR2(30))';
EXECUTE IMMEDIATE 'INSERT INTO MY_TABLE1 SELECT * FROM TABLE(:T)' USING MT; -- OK
-- One step
EXECUTE IMMEDIATE 'CREATE TABLE MY_TABLE2 AS SELECT * FROM TABLE(:T)' USING MT; -- ERROR ORA-22905
END;
The real code for the SELECT * FROM TABLE(:T) is dynamic (main table name is temporary) and slow. That's why I try to avoid creating the table in two steps (as done with MY_TABLE1). Also with two steps I can't use SELECT * but I have to specify all the columns (variable amount and over 100 columns).
There is likely a way to completely avoid this issue. Skip the bulk collect and use a simple CREATE TABLE MY_TABLE AS SELECT * FROM DUAL; That may be an over-simplification of the real logic to gather the data. But there is almost always a way to bypass a bulk collect and store the data directly in an object with just SQL.
If a PL/SQL solution is truly needed, the error ORA-22905: cannot access rows from a non-nested table item can be avoided by creating an object type and creating the table based on that type. This may not solve the performance issue, but at least this avoids the need to re-specify all the columns in the table DDL.
CREATE TYPE MY_TABLE_OBJECT IS OBJECT
(
A VARCHAR2(30)
);
CREATE TYPE MY_TABLE_TYPE2 AS TABLE OF VARCHAR2(30);
DECLARE
MT MY_TABLE_TYPE2;
BEGIN
SELECT * BULK COLLECT INTO MT FROM DUAL;
EXECUTE IMMEDIATE 'CREATE TABLE MY_TABLE2 OF MY_TABLE_OBJECT';
EXECUTE IMMEDIATE 'INSERT INTO MY_TABLE2 SELECT * FROM TABLE(:T)' USING MT;
END;
/

How do I use CREATE OR REPLACE?

Am I correct in understanding that CREATE OR REPLACE basically means "if the object exists, drop it, then create it either way?"
If so, what am I doing wrong? This works:
CREATE TABLE foo (id NUMBER,
title VARCHAR2(4000) DEFAULT 'Default Title')
And this doesn't (ORA-00922: missing or invalid option):
CREATE OR REPLACE TABLE foo (id NUMBER,
title VARCHAR2(4000) DEFAULT 'Default Title')
Am I doing something stupid? I don't seem to be able to find much documentation about this syntax.
This works on functions, procedures, packages, types, synonyms, trigger and views.
Update:
After updating the post for the third time, I'll reformulate this:
This does not work on tables :)
And yes, there is documentation on this syntax, and there are no REPLACE option for CREATE TABLE.
One of the nice things about the syntax is that you can be sure that a CREATE OR REPLACE will never cause you to lose data (the most you will lose is code, which hopefully you'll have stored in source control somewhere).
The equivalent syntax for tables is ALTER, which means you have to explicitly enumerate the exact changes that are required.
EDIT:
By the way, if you need to do a DROP + CREATE in a script, and you don't care for the spurious "object does not exist" errors (when the DROP doesn't find the table), you can do this:
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE owner.mytable';
EXCEPTION
WHEN OTHERS THEN
IF sqlcode != -0942 THEN RAISE; END IF;
END;
/
There is no create or replace table in Oracle.
You must:
DROP TABLE foo;
CREATE TABLE foo (....);
CREATE OR REPLACE can only be used on functions, procedures, types, views, or packages - it will not work on tables.
Following script should do the trick on Oracle:
BEGIN
EXECUTE IMMEDIATE 'drop TABLE tablename';
EXCEPTION
WHEN OTHERS THEN
IF sqlcode != -0942 THEN RAISE;
END IF;
END;
-- To Create or Replace a Table we must first silently Drop a Table that may not exist
DECLARE
table_not_exist EXCEPTION;
PRAGMA EXCEPTION_INIT (table_not_exist , -00942);
BEGIN
EXECUTE IMMEDIATE('DROP TABLE <SCHEMA>.<TABLE NAME> CASCADE CONSTRAINTS');
EXCEPTION WHEN table_not_exist THEN NULL;
END;
/
Does not work with Tables, only functions etc.
Here is a site with some examples.
A usefull procedure for oracle databases without using exeptions (under circumstances you have to replace user_tables with dba_tables and/or constrain the tablespace in the query):
create or replace procedure NG_DROP_TABLE(tableName varchar2)
is
c int;
begin
select count(*) into c from user_tables where table_name = upper(tableName);
if c = 1 then
execute immediate 'drop table '||tableName;
end if;
end;
If you are doing in code then first check for table in database
by using query
SELECT table_name
FROM user_tables
WHERE table_name = 'XYZ'
if record found then truncate table otherwise create Table
Work like Create or Replace.
You can use CORT (www.softcraftltd.co.uk/cort). This tool allows to CREATE OR REPLACE table in Oracle.
It looks like:
create /*# or replace */ table MyTable(
... -- standard table definition
);
It preserves data.
So I've been using this and it has worked very well: - it works more like a DROP IF EXISTS but gets the job done
DECLARE
VE_TABLENOTEXISTS EXCEPTION;
PRAGMA EXCEPTION_INIT(VE_TABLENOTEXISTS, -942);
PROCEDURE DROPTABLE(PIS_TABLENAME IN VARCHAR2) IS
VS_DYNAMICDROPTABLESQL VARCHAR2(1024);
BEGIN
VS_DYNAMICDROPTABLESQL := 'DROP TABLE ' || PIS_TABLENAME;
EXECUTE IMMEDIATE VS_DYNAMICDROPTABLESQL;
EXCEPTION
WHEN VE_TABLENOTEXISTS THEN
DBMS_OUTPUT.PUT_LINE(PIS_TABLENAME || ' NOT EXIST, SKIPPING....');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
RAISE;
END DROPTABLE;
BEGIN
DROPTABLE('YOUR_TABLE_HERE');
END DROPTABLE;
/
Hope this helps
Also reference:
PLS-00103 Error in PL/SQL Developer
'Create or replace table' is not possible. As others stated, you can write a procedure and/or use begin execute immediately (...). Because I don't see an answer with how to (re)create the table, I putted a script as an answer.
PS: in line of what jeffrey-kemp mentioned: this beneath script will NOT save data that is already present in the table you are going to drop. Because of the risk of loosing data, at our company it is only allowed to alter existing tables on the production environment, and it is not allowed to drop tables. By using the drop table statement, sooner or later you will get the company police standing at your desk.
--Create the table 'A_TABLE_X', and drop the table in case it already is present
BEGIN
EXECUTE IMMEDIATE
'
CREATE TABLE A_TABLE_X
(
COLUMN1 NUMBER(15,0),
COLUMN2 VARCHAR2(255 CHAR),
COLUMN3 VARCHAR2(255 CHAR)
)';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -955 THEN -- ORA-00955: object name already used
EXECUTE IMMEDIATE 'DROP TABLE A_TABLE_X';
END IF;
END;
I would do something like this
begin
for i in (select table_name from user_tables where table_name = 'FOO') loop
execute immediate 'drop table '||i.table_name;
end loop;
end;
execute immediate 'CREATE TABLE FOO (id NUMBER,
title VARCHAR2(4000)) ';
If this is for MS SQL.. The following code will always run no matter what if the table exist already or not.
if object_id('mytablename') is not null //has the table been created already in the db
Begin
drop table mytablename
End
Create table mytablename (...

Resources