PL/SQL : Ignoring -942 EXCPETION issues - still bombs on PACKAGE Compilation - oracle

I have a Procedure within a Package that I create a "Temporary" table for processing. I wrote code to IGNORE the -942 error because at the end of the Procedure I drop the table. Therefore when it runs again, the table wouldn't exist and it needs to ignore that error.
However, when I compile the code (without the table present) it still bombs on me even with the EXCEPTION logic claiming the "table does not exist" which is true, but I don't care.
What am I doing wrong?
--Temp Table Setup
V_TEMP_COUNT INT;
NO_SUCH_TABLE EXCEPTION;
PRAGMA EXCEPTION_INIT (NO_SUCH_TABLE, -942);
--Declare Record Types
REC_P170201 CUR_P170201%ROWTYPE;
BEGIN --[SP_LOAD_A_DEPARTMENT_PVSS]
------------------------------------------------------------------------------------------
--PART P17.01 INITIALIZE --
------------------------------------------------------------------------------------------
BEGIN
SELECT COUNT (*) INTO V_TEMP_COUNT
FROM USER_TABLES
WHERE TABLE_NAME = UPPER('F_DEPARTMENT_ADT');
EXCEPTION
WHEN NO_SUCH_TABLE THEN
NULL;
WHEN OTHERS THEN
RAISE;
END;
-- other stuff
END SP_LOAD_A_DEPARTMENT_PVSS;

The following block of code will not raise the NO_SUCH_TABLE error when there is no table named F_DEPARTMENT_ADT
BEGIN
SELECT COUNT (*) INTO V_TEMP_COUNT
FROM USER_TABLES
WHERE TABLE_NAME = UPPER('F_DEPARTMENT_ADT');
EXCEPTION
WHEN NO_SUCH_TABLE THEN
NULL;
WHEN OTHERS THEN
RAISE;
END;
That is because the query is asking the view USER_TABLES if there is a row whose table_name column value is UPPER('F_DEPARTMENT_DAT'). Instead of a NO_SUCH_TABLE error, the error you will get is a no data found err (ORA-1403).

You cannot catch compilation errors. One way to handle the none existence of a table is to perform the SELECT-Statement as dynamic sql, so it does get compiled on runtime:
EXECUTE IMMEDIATE 'SELECT COUNT (*) FROM USER_TABLES WHERE TABLE_NAME = UPPER(''F_DEPARTMENT_ADT'')'
INTO V_TEMP_COUNT;
But I think the code you posted is not the one that throws your error at compile time. It might be the declaration of REC_P170201. So in this case I would recommend you, to create your own record type with only the columns you need in your procedure and then just select the columns you need in the dynamic sql.

SELECT COUNT (*) INTO V_TEMP_COUNT
FROM USER_TABLES
WHERE TABLE_NAME = UPPER('F_DEPARTMENT_ADT');
Is not going to fail if F_DEPARTMENT_ADT does not exist. It will either return a value into V_TEMP_COUNT of 0 or 1. You will can use that to make decisions in the rest of your code.
If you need your code to actually use your table then it will need to be done in dynamic SQL so that the compilation of your PL/SQL is not dependent on the compilation of your SQL (which will sometimes fail).
Saying that, I 100% agree with EdStevens: you're after a global temporary table here.

Related

oracle exception not going to exception block

I need to update some code which uses dynamic sql and potentially could have duplicate column names in the columns of the insert statement.
So I wanted to handle this, ORA-00957: Duplicate Column name. This error does not get handled by the most generic "when others" in the exception block. If I make it test a unique constraint violation it does.
Following some test code:
create table animal (id number , animal_type number,
animal_name varchar2(20), constraint id primary key(id));
-----------------------------------------------------
begin
for i in 1.. 100 loop
insert into animal(id, animal_type, animal_name)
values(i,floor(dbms_random.value(1,30)),'animal'||i);
end loop;
end;
-----------------------------------------------------
DECLARE
-- e_duplicate_column exception;
-- pragma exception_init(e_duplicate_column,-957);
BEGIN
insert into animal(id, animal_name, animal_name)
values(1000003, 'animal 1000003', 'animal 1000003');
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(SQLCODE);
dbms_output.put_line(SQLERRM);
END;
I was trying to get the codes here as the pragma wasn't working(i.e. arriving) either. But that's not happening if it doesn't even get to "when others".
Any insights are appreciated.
Cheers, Robbert
PS oracle 12C, tried on sqldeveloper and toad
Your test code does not use dynamic SQL, which is required to generate an ORA-00957 error. Without dynamic SQL, Oracle will throw the ORA-00957 when it compiles your block, which I think you are misinterpreting as Oracle actually running you block and skipping the exception handler.
Try this instead as a test (make sure you have DBMS output enabled in your client!):
DECLARE
-- e_duplicate_column exception;
-- pragma exception_init(e_duplicate_column,-957);
BEGIN
execute immediate q'[insert into animal(id, animal_name, animal_name) values(1000003, 'animal 1000003', 'animal 1000003')]';
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(SQLCODE);
dbms_output.put_line(SQLERRM);
END;
-957
ORA-00957: duplicate column name

Oracle pl/sql Procedure to update table - exception handling

This is my first query on PL/SQL and I did spend an hour trying to find answers on the net, anyway - here it goes.
I'm writing a procedure to update a table and it all works fine, however when I typed in to update a job_id that doesn't exist, I expected my exception handling to tell me that the job_id is invalid, however I got no error message.
My code is as follows:
CREATE OR REPLACE PROCEDURE UPD_JOB(p_job_id jobs.job_id%TYPE, p_jobnew jobs.job_title%TYPE)
IS
BEGIN
UPDATE JOBS SET job_title =p_jobnew WHERE JOB_ID = p_job_id;
EXCEPTION
WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('No rows have been updated');
END;
/
I then tried to update a job_id that I knew didn't exist to see if the exception works by typing the following
EXECUTE UPD_JOB('ABCXXX','WILL FAIL');
From "Handling PL/SQL Errors":
NO_DATA_FOUND
A SELECT INTO statement returns no rows, or your program references a deleted element in a nested table or an uninitialized element in an index-by table. SQL aggregate functions such as AVG and SUM always return a value or a null. So, a SELECT INTO statement that calls an aggregate function never raises NO_DATA_FOUND. The FETCH statement is expected to return no rows eventually, so when that happens, no exception is raised.
You're not using a statement that would cause a NO_DATA_FOUND exception to be raised.
Maybe you can use SQL%ROWCOUNT. From "Performing SQL Operations from PL/SQL":
To find out how many rows are affected by DML statements, you can check the value of SQL%ROWCOUNT...
CREATE OR REPLACE PROCEDURE UPD_JOB (p_job_id jobs.job_id%TYPE,
p_jobnew jobs.job_title%TYPE)
IS
BEGIN
UPDATE JOBS
SET job_title = p_jobnew
WHERE JOB_ID = p_job_id;
IF SQL%ROWCOUNT = 0 THEN
DBMS_OUTPUT.PUT_LINE('No rows have been updated');
END IF;
END;
/

In Oracle, how to catch exception when table used for the loop doesn't exists

I have this code:
BEGIN
FOR
U1 IN (SELECT * FROM SOME_USER.SOME_TABLE)
LOOP
BEGIN
-- do something;
END;
END LOOP;
END;
My problem is that sometimes SOME_USER.SOMETABLE do not exists but I want the rest of the script to be run. I know that checking if the table exists before running the code (in a IF ... THEN block) will not work because SELECT * FROM SOME_USER.SOME_TABLE is evaluated at compile time.
So another avenue is to run the SELECT with EXECUTE IMMEDIATE. This way it will be evaluated at run time and I would be able to catch the exception. Unfortunately I can't find a way to use EXECUTE IMMEDIATE with my U1 IN loop. How I should achieve this?
I'm on Oracle 11g and the SQL script is run from a batch script on Windows.
You can use the 'OPEN FOR' syntax:
DECLARE
CUR SYS_REFCURSOR;
<variables or record type> -- declare as appropriate
BEGIN
OPEN CUR FOR 'SELECT * FROM SOME_USER.SOME_TABLE';
LOOP
FETCH CUR INTO <variables or record type>;
EXIT WHEN CUR%NOTFOUND;
-- do something with variables or record
END LOOP;
CLOSE CUR;
END;
/
You need to fetch each row into variables or a record type, you can't use %ROWTYPE as the table still won't exist; and you can change to do bulk fetches if that's appropriate for your data volumes.
If you run that you'll still get ORA-00942, but if this is in a stored program you won't get it until run time, and you can now add an IF block to check for the table's existence before the OPEN.
Having a data model where objects may or may not exist at run-time seems rather fishy though...
Proposed solution with cursor is fine, I would add an exception handling for this particular exception : Table or view does not exist ORA-00942.
DECLARE
e_missing_t EXCEPTION;
pragma exception_init (e_missing_t,-942);
something number; --some variable you need to fetch to
CUR SYS_REFCURSOR;
BEGIN
OPEN CUR FOR 'SELECT * FROM SOME_USER.SOME_TABLE';
LOOP
FETCH CUR INTO something;
EXIT WHEN CUR%NOTFOUND;
-- do something with variables or record
END LOOP;
CLOSE CUR;
EXCEPTION
WHEN e_missing_t THEN
dbms_output.put_line('some_table is missing');
END;
/
You could possibly use a workaround -
Create a nested table type and store the results of the SELECT in it. Use that type to loop through values.
So,
SELECT data_obj(COL1, COL2) bulk collect into data_tbl_typ from data_table;
This part can go in the dynamic sql. (Remember to use bind variables)
And then just loop through this nested table type in your procedure.
Use the DBMS_SQL package to run the query.
Follow the examples in this Oracle documentation:
http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_sql.htm#sthref6147
pl/sql has the exception clause for that. OTHERS catches pretty much everything. You can deal with the exception in the function, or print a message and pass it back to the main. Break your function up into smaller functions and have each one catch it's own exception.
BEGIN
FOR
U1 IN (SELECT * FROM SOME_USER.SOME_TABLE)
LOOP
BEGIN
-- do something;
END;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE ('Oh well. The table isn't there.');
--RAISE;
END;

"Select ... into .." statement fails with "table or view does not exist" even though it should not be run

I have the following PL/SQL script that I'm trying to run through SQL*Plus:
DECLARE
table_exists number;
sequence_exists number;
sequence_start number;
BEGIN
select count(*) into sequence_exists
from all_sequences
where sequence_name='DBSEQ';
select count(*) into table_exists
from dba_tables
where table_name='DBTABLE';
IF sequence_exists = 0 AND table_exists > 0 THEN
select MAX(ID) + 1 into sequence_start from DBTABLE;
ELSE
sequence_start := 1;
END;
IF sequence_exists = 0 THEN
execute immediate 'CREATE SEQUENCE DBSEQ
start with ' || sequence_start || '
increment by 1
nomaxvalue';
END IF;
END;
(this is somewhat minimized, to show the parts that are actually failing).
The problem is that the row
select MAX(ID) + 1 into sequence_start from DBTABLE;
fails with "PL/SQL: ORA-00942: table or view does not exist", because the table does not exist (and I've already verified that the table_exists variable is actually 0). So basically, the select runs (or at least fails) even though the if-clause does not execute. I've also verified that the if-clause indeed fails by replacing the select with e.g. creating a table, which does not exist after running the script.
I did also try catching exceptions instead, doing something like this:
BEGIN
select MAX(ID) + 1 into sequence_start from DBTABLE;
EXCEPTION
WHEN OTHERS THEN
sequence_start := 1;
END;
but that produced the same error.
So, is there anything special with the select statement, that makes it run before anything else? And how should I solve my problem?
The problem is that Oracle has to compile the block before it can run it. Part of compiling the block involves resolving all the static references. If you have a reference to a table that does not exist, the block will fail to compile so it cannot be run. Your exception handler doesn't do anything because it is a compilation error, not an execution error.
If you want your block to refer to a table that may not exist at the time the block is compiled, you would need to use dynamic SQL.
EXECUTE IMMEDIATE 'select max(id) from dbtable'
INTO sequence_start;
That will allow the block to compile successfully since Oracle doesn't need to resolve references in dynamic SQL statements. If your code tries to execute the dynamic SQL statement when dbtable doesn't exist, you'll get a run-time error (which you could catch with an exception handler).

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