Delete rows only if the table exists - sql

I need to run a delete statement on my database if a table was previously created.
The problem is - I can't just run the delete statement, since the product is not on every client's productive environments - therefore, they don't have the table where I want to run the delete statement, and would end up with an error 00942. 00000 - "table or view does not exist".
An example:
I would like to run something like this:
IF EXISTS (TABLE TB_FIELD)
DELETE FROM TB_FIELD WHERE ID = '213';
If there isn't a generic statement, I would like one that would run for Oracle databases

Here's one for Oracle. This assumes the current user owns the table. If you're updating someone else's table you'll need to swap out user_tables with dba_tables.
declare
table_name_l user_tables.table_name%type;
begin
select table_name into table_name_l from user_tables where table_name = 'TB_FIELD';
-- if we didn't raise an exception the table must exist
execute immediate 'delete from tb_field where id = :1' using '213';
exception
when no_data_found then
-- do nothing - table doesn't exist
null;
end;

Simplest way is to catch and ignore the "table not found" exception:
declare
l_id number := 12345;
begin
execute immediate 'delete tb_field where id=:1' using l_id;
exception
when others then
if sqlcode != -942 /*table or view does not exist*/ then
raise;
end if;
end;
/

Related

Oracle rename partition if exists

I need to create a statement that can be run to rename a partition only if specific partition name exists and if not then continue on to execute other code.
basic command = ALTER TABLE TEST RENAME PARTITION P1 TO P2:
I have looked at the following but have not come up with a solution
Using IF ELSE in Oracle
https://docs.oracle.com/cd/B28359_01/appdev.111/b28370/controlstructures.htm
You could check whether the partition exists within the table USER_TAB_PARTITIONS:
DECLARE
v_p1_exists AS NUMBER;
v_p2_exists AS NUMBER;
BEGIN
SELECT COUNT(*)
INTO v_p1_exists
FROM user_tab_partitions
WHERE table_name = 'TEST'
AND partition_name = 'P1';
SELECT COUNT(*)
INTO v_p2_exists
FROM user_tab_partitions
WHERE table_name = 'TEST'
AND partition_name = 'P2';
IF (v_p1_exists <> 0) AND (v_p2_exists = 0) THEN
EXECUTE IMMEDIATE 'ALTER TABLE TEST RENAME PARTITION P1 TO P2';
END;
END;
I depends on your requirements but a basic procedure would be this one:
DECLARE
PARTITION_DOES_NOT_EXIST EXCEPTION;
PRAGMA EXCEPTION_INIT(PARTITION_DOES_NOT_EXIST, -2149);
BEGIN
BEGIN
EXECUTE IMMEDIATE 'ALTER TABLE TEST RENAME PARTITION P1 TO P2';
EXCEPTION
WHEN PARTITION_DOES_NOT_EXIST THEN NULL;
END;
... ohter commands
END;
One option is to enclose ALTER TABLE into its own BEGIN-END block, with appropriate exception handling section. Mine is kind of stupid (WHEN OTHERS, eh?); feel free to rewrite it to be meaningful - it is just to show how to do it.
So: if ALTER TABLE fails, it'll raise some error; it'll be captured, handled, and code will continue its execution.
begin
begin
execute immediate 'alter table test rename partition p1 to p2';
exception
when others then
-- ignore errors
null;
end;
-- continue to execute other code
...
end;

Drop table having name: <table_name>_(sysdate-1) only if the table exist

I need to drop a table only if the table exist, can do it using plsql block
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE <table_name>;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
END
But in my case the table name has sysdate (02062017) eg table001_02072017 and i need to delete all such tables with sysdate-1.
How can i do so?
You can find the table with the given pattern from user_tables dictionary table and loop on the result to drop each one by one.
begin
for t in (select table_name from user_tables
where table_name like '%\_'||to_char(sysdate-1,'mmddyyyy') escape '\')
loop
execute immediate 'drop table ' || t.table_name;
end loop;
exception
/* Handle your exceptions here. */
end;
/
Using WHEN OTHERS in your exception handling is discouraged. You should explicitly handle the errors.
Dynamic sql will help you here just use
execute immediate 'drop table ' || table_name;
inside your procedure

How to get old values in Compound Trigger Update in oracle

I searched about my issue but couldn't got any solution yet so i am posting it here. I am using Oracle Database 12c Enterprise Edition Release 12.1.0.1.0 - 64bit Production.
I have a Main_Table with suppose 10 Records and I have a Log_Table to log (insert) the old and new values on update of new table.
I am using a compound trigger (to avoid mutating errors) to loop through all columns of "Main_Table" dynamically and get the updating row records by filtering through new . Primarykey (UID)).
I hope i am using Compound trigger right.
I am not using :old and :new as I am looping through all columns dynamically and need to match the column values.
But it is again giving me mutating error:
Error report -
SQL Error: ORA-04091: table Main_Table is mutating, trigger/function may not see it
ORA-06512: at "Schema.TRG_TEST", line 87
ORA-04088: error during execution of trigger 'Schema.TRG_TEST'
04091. 00000 - "table %s.%s is mutating, trigger/function may not see it"
*Cause: A trigger (or a user defined plsql function that is referenced in
this statement) attempted to look at (or modify) a table that was
in the middle of being modified by the statement which fired it.
*Action: Rewrite the trigger (or function) so it does not read that table.
Below is my trigger code:
create or replace TRIGGER TRG_TEST
FOR INSERT or UPDATE ON Main_Table
COMPOUND TRIGGER
TYPE NUMBER_TABLE IS TABLE OF NUMBER;
tblTABLE2_IDS NUMBER_TABLE;
TYPE VARCHAR_TABLE IS TABLE OF VARCHAR(2000);
tblTABLE3_IDS VARCHAR_TABLE;
TYPE VARCHAR_TABLE1 IS TABLE OF VARCHAR(2000);
tblTABLE4_IDS VARCHAR_TABLE1;
vcount NUMBER;
colCount NUMBER;
colCountAfter NUMBER;
vvalue VARCHAR2(4000);
vcolumn VARCHAR2(4000);
sql1 VARCHAR2(4000);
dynamicq varchar(4000);
testv varchar(2000);
testv1 varchar(2000);
ssql varchar(2000);
ssql1 varchar(2000);
maxsiteid NUMBER;
newsid varchar(2000);
newstid varchar(2000);
newuid varchar(2000);
--Executed before DML statement
BEFORE STATEMENT IS
BEGIN
tblTABLE2_IDS := NUMBER_TABLE();
tblTABLE3_IDS:= VARCHAR_TABLE();
tblTABLE4_IDS:= VARCHAR_TABLE1();
IF UPDATING THEN
dbms_output.put_line('Before Statement - In Updating');
--dbms_output.put_line('Before Each Row - In Updating');
-- tblTABLE2_IDS.EXTEND;
--tblTABLE2_IDS(tblTABLE2_IDS.LAST) := :new.UID;
END IF;
END BEFORE STATEMENT;
--Executed before each row change- :NEW, :OLD are available
BEFORE EACH ROW IS
BEGIN
IF UPDATING THEN
dbms_output.put_line('Before Each Row - In Updating');
tblTABLE2_IDS.EXTEND;
tblTABLE2_IDS(tblTABLE2_IDS.LAST) := :new.UID;
-- FOR columnlist IN
--(SELECT COLUMN_NAME AS COLUMN_NAME FROM all_tab_columns WHERE lower(TABLE_NAME) = 'Main_Table'
-- AND lower(COLUMN_NAME) NOT IN ( 's_id' ,'msid' ,'st' ,'u_id' ,'db_flag' ))
--LOOP
--colCount:=colCount+1;
--ssql1:='select '||columnlist.COLUMN_NAME||' from Main_Table where UID='||tblTABLE2_IDS(tblTABLE2_IDS.LAST)||'';
--dbms_output.put_line(ssql1);
--execute immediate ssql1 into testv;
--tblTABLE3_IDS(colCount):=testv;
--dbms_output.put_line(testv);
--END LOOP;
END IF;
END BEFORE EACH ROW;
--Executed aftereach row change- :NEW, :OLD are available
AFTER EACH ROW IS
BEGIN
IF UPDATING THEN
dbms_output.put_line('After Each Row - In Updating');
FOR columnlist IN
(SELECT COLUMN_NAME AS COLUMN_NAME FROM all_tab_columns WHERE lower(TABLE_NAME) = 'Main_Table'
AND lower(COLUMN_NAME) NOT IN ( 's_id' ,'msid' ,'st' ,'u_id' ,'db_flag' ))
LOOP
colCount:=colCount+1;
ssql1:='select '||columnlist.COLUMN_NAME||' from Main_Table where UID='||tblTABLE2_IDS(tblTABLE2_IDS.LAST)||'';
dbms_output.put_line(ssql1);
execute immediate ssql1 into testv;
tblTABLE3_IDS(colCount):=testv;
dbms_output.put_line(testv);
END LOOP;
END IF;
END AFTER EACH ROW;
--Executed after DML statement
AFTER STATEMENT IS
BEGIN
IF UPDATING THEN
dbms_output.put_line('After Statement - In Updating');
FOR columnlist IN
(SELECT COLUMN_NAME AS COLUMN_NAME FROM all_tab_columns WHERE lower(TABLE_NAME) = 'Main_Table'
AND lower(COLUMN_NAME) NOT IN ( 's_id' ,'msid' ,'st' ,'u_id' ,'db_flag' ))
LOOP
colCountAfter:=colCountAfter+1;
dbms_output.put_line('loop started');
ssql1:='select '||columnlist.COLUMN_NAME||' from Main_Table where UID='||tblTABLE2_IDS(tblTABLE2_IDS.LAST)||'';
execute immediate ssql1 into testv1;
dbms_output.put_line(testv);
tblTABLE3_IDS(colCountAfter):=testv1;
IF ((testv) IS NOT NULL) THEN
FOR i IN tblTABLE3_IDS.FIRST..tblTABLE2_IDS.LAST LOOP
dbms_output.put_line('Values No :' ||i||' is ' || tblTABLE3_IDS(i) || ' and ' ||tblTABLE4_IDS(i));
IF(tblTABLE3_IDS(i)=tblTABLE4_IDS(i)) THEN
dbms_output.put_line(testv1);
ELSE
-- dbms_output.put_line('select :new.'|| columnlist.COLUMN_NAME||' from dual');
dbms_output.put_line(testv1);
INSERT INTO Log_Table
(
user_id,
log_action,
log_table_name,
schema_name,
log_column_name,
col_old_val,
col_new_val,
ne_type,
ne_id,
system_id
)
VALUES
(
newuid,
'UPDATE',
'Main_Table',
'SCHEMA'
,columnlist.COLUMN_NAME
,tblTABLE3_IDS(i)
,tblTABLE4_IDS(i)
,'S'
,newstid
,newsid
);
END IF;
END LOOP;
END IF;
END LOOP;
END IF;
END AFTER STATEMENT;
END TRG_TEST;
Initially i had tried accessing the updating table in "Before Each Row" then i tried accessing it in "After Each Row", same error in both cases.
I am struggling to find a solution to it even after using compound trigger,however i achieved the same for insert.
Can anyone help in how to achieve it. Thanks in Advance.
Usually an after statement row level trigger is sufficient to fill a logging table.
Old and new column values are available in the :old and :new pseudo records.
A very basic example:
create or replace trigger my_trigger
after insert or update on my_table
for each row
declare
begin
if inserting
then
insert into logging table
(id
,my_column_old
,my_column_new)
values
(:new.id
,null -- no old value in case of insert
,:new.my_column);
else
insert into logging table
(id
,my_column_old
,my_column_new)
values
(:new.id
,:old.my_column
,:new.my_column);
end if;
end;
As I understand from one of your comments you want track history of new added column(users can add and drop column) and you want not recompile trigger.
Then you can use Oracle Total Recall
Happily you have Oracle 12c EE(most bugs are fixed).

Oracle SQL - If Exists, Drop Table & Create

Can some one please guide me what's wrong with this query? In SQL Server we just check the presence of the Object_ID of a table to drop it and re-create it. I am new to Oracle and wrote this query:
declare Table_exists INTEGER;
BEGIN
Select count(*) into Table_exists from sys.all_tables where table_name='TABLENAME1';
EXCEPTION
WHEN NO_DATA_FOUND
THEN
Table_Exists :=0;
if(table_exists)=1
Then
Execute Immediate 'Drop Table TABLENAME1;'
'Create Table TABLENAME1;';
DBMS_OUTPUT.PUT_LINE('Table Dropped and Re-Created!');
Else
Execute Immediate 'Create Table TABLENAME1;';
DBMS_OUTPUT.PUT_LINE('New Table Created!');
END IF;
END;
I get the output - ANONYMOUS BLOCK COMPLETED, but the table is not created. The table was previously existing, so I dropped it to check if the PL/SQL is actually creating the table, but NO. What is wrong here? What am I missing? Please guide.
When you are using all_tables filter the results for your
schema by adding where owner = 'your_schema'
or use sys.user_tables
ALL_TABLES describes the relational tables accessible to the current user
USER_TABLES describes the relational tables owned by the current user.
When use execute_emmidiate remove the ; from the query;
Modified query;
DECLARE
Table_exists INTEGER;
BEGIN
Select count(*) into Table_exists from sys.user_tables where table_name='TABLENAME1';
--or
--Select count(*) into Table_exists from sys.all_tables
--where table_name='TABLENAME1' and owner = 'your_DB';
if table_exists = 1 Then
Execute Immediate 'Drop Table TABLENAME1';
Execute Immediate 'Create Table TABLENAME1(num number)';
DBMS_OUTPUT.PUT_LINE('Table Dropped and Re-Created!');
Else
Execute Immediate 'Create Table TABLENAME1(num number)';
DBMS_OUTPUT.PUT_LINE('New Table Created!');
END IF;
END;
First note:
Select count(*) into Table_exists
from sys.all_tables
where table_name = 'TABLENAME1';
will always return one row. You don't need the exception handling.
My best guess is that you have more than one table called TABLENAME1. Run this query to find out:
Select *
from sys.all_tables
where table_name = 'TABLENAME1';
Oracle stores tables from all owners that you can access. You might also want to check OWNER_NAME in the where clause.
However, you seem to understand exception handling. So, just drop the table, ignore any errors, and then recreate the table.
The EXCEPTION clause lasts till the next END and not just the next statement. If you want to continue after catching the exception you need to add an additional BEGIN/END:
declare
Table_exists INTEGER;
BEGIN
BEGIN
Select count(*) into Table_exists from sys.all_tables where table_name='TABLENAME1';
EXCEPTION
WHEN NO_DATA_FOUND THEN
Table_Exists :=0;
END;
if(table_exists)=1 Then
Execute Immediate 'Drop Table TABLENAME1;'
Execute Immediate 'Create Table TABLENAME1;';
DBMS_OUTPUT.PUT_LINE('Table Dropped and Re-Created!');
Else
Execute Immediate 'Create Table TABLENAME1;';
DBMS_OUTPUT.PUT_LINE('New Table Created!');
END IF;
END;
As pointed out by Gordon, the EXCEPTION clause is not really needed in this case since count(*) will always return one row. So the following is sufficient:
declare
Table_exists INTEGER;
BEGIN
Select count(*) into Table_exists from sys.all_tables where table_name='TABLENAME1';
if(table_exists)=1 Then
Execute Immediate 'Drop Table TABLENAME1;'
Execute Immediate 'Create Table TABLENAME1;';
DBMS_OUTPUT.PUT_LINE('Table Dropped and Re-Created!');
Else
Execute Immediate 'Create Table TABLENAME1;';
DBMS_OUTPUT.PUT_LINE('New Table Created!');
END IF;
END;

Drop Table Exception Intermittently doesn't work

I have been wrapping my "drop tables" in the following block to avoid raising 942 errors if the table doesn't exist:
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE MY_TABLE1';
EXECUTE IMMEDIATE 'DROP TABLE MY_TABLE2';
EXECUTE IMMEDIATE 'DROP TABLE MY_TABLE3';
EXECUTE IMMEDIATE 'DROP TABLE MY_TABLE4';
EXECUTE IMMEDIATE 'DROP TABLE MY_TABLE5';
EXECUTE IMMEDIATE 'DROP TABLE MY_TABLE6';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN RAISE;
END IF;
END;
This works great most of the of time. However, intermittently it will seem to refuse to drop this table or that. It's not always the same table, and it doesn't always happen in any particular set of queries. It just happens...sometimes...for reasons I can't explain, hence my asking this question.
Has anyone else experienced this, and if so, what did you do about it?
Most likely cause I can think of is the table is locked (outstanding commits) in another session. What error is reported?
Also there is a problem with your script - if TABLE1 has already been dropped, TABLE2...6 won't get dropped because your first DROP will jump to the exception.
Better to do this:
DECLARE
PROCEDURE drp ( tName IN VARCHAR2 ) IS
BEGIN
EXECUTE IMMEDIATE 'drop table ' || tName;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN RAISE;
END IF;
END;
BEGIN
drp ( 'TABLE1' );
drp ( 'TABLE2' );
drp ( 'TABLE3' );
-- etc
END;