multiple drop trigger statements not working - sql

The below PL/SQL is not working as expected
The ask is to delete triggers if it exists. The below is deleting only the first trigger.
BEGIN
EXECUTE IMMEDIATE 'DROP TRIGGER ' || 'trigger1';
EXECUTE IMMEDIATE 'DROP TRIGGER ' || 'trigger2';
EXECUTE IMMEDIATE 'DROP TRIGGER ' || 'trigger3';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -4080 THEN
RAISE;
END IF;
END;

If the first or second trigger doesn't exist then you'll drop to the exception handler. It won't then return to the original program flow, even when you ignore the error, and it will never see or attempt the later dynamic calls.
You need an exception handler around each individual drop.
To reduce repetition you could do that in a loop, or with a local procedure:
DECLARE
PROCEDURE drop_trigger(p_trigger_name user_triggers.trigger_name%type) IS
BEGIN
EXECUTE IMMEDIATE 'DROP TRIGGER ' || p_trigger_name;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -4080 THEN
RAISE;
END IF;
END drop_trigger;
BEGIN
drop_trigger('trigger1');
drop_trigger('trigger2');
drop_trigger('trigger3');
END;
/
db<>fiddle
You can also add debugging, with dbms_output, to see what's happening - as in this modified db<>fiddle.

Related

Display the output of EXECUTE IMMEDIATE statement

I have a stored procedure to truncate the table whose name is passed as a parameter to the procedure.
create or replace procedure delete_data_from_table(table_id VARCHAR2)
is
cursor table_cur is
SELECT table_name FROM all_tables WHERE table_name LIKE '%' || table_id || '%';
tab_name VARCHAR2(25);
BEGIN
open table_cur;
LOOP
FETCH table_cur into tab_name;
exit when table_cur%notfound;
EXECUTE IMMEDIATE 'TRUNCATE TABLE ' || tab_name;
END LOOP;
close table_cur;
END;
I want to display the output of the execute immediate statement using DBMS_OUTPUT.PUT_LINE. Is it possible to do so?
Thanks in advance.
There is no native output from execute immediate or the statement you are running. When you truncate a table in a client it will usually report something like:
Table truncated.
or
Table MY_TABLE truncated.
or similar; but those messages are generated by the client running the command, not by the database.
You can just generate whatever message you want in your procedure, e.g.:
...
LOOP
FETCH table_cur into tab_name;
exit when table_cur%notfound;
EXECUTE IMMEDIATE 'TRUNCATE TABLE ' || tab_name;
DBMS_OUTPUT.PUT_LINE('Table ' || tab_name || ' truncated.');
END LOOP;
...
If the truncate statement fails for any reason then it will throw an exception and it won't reach the dbms_output line.
For other statement types you can optionally use SQL%ROWCOUNT to report the number of rows inserted/updated/merged/deleted to mimic what your client might show for those, but that doesn't apply for truncation.
Bear in mind though that someone else running your code might not have display of those messages enabled.

Getting failed id's from for all save exceptions

I am trying to update salary of employees using forall. Whenever any error occurs while updating I need to save for which employee id error has occurred.
But it gives following error while compiling
Error(14,24): PLS-00201: identifier 'INDX' must be declared
Below is my code
PROCEDURE PROC1 (V_EMP_ID DBMS_SQL.NUMBER_TABLE)
IS
lv_error_string VARCHAR2(4000);
BEGIN
FORALL INDX IN V_EMP_ID.FIRST..V_EMP_ID.LAST SAVE EXCEPTIONS
EXECUTE IMMEDIATE 'UPDATE EMPLOYEES SET SALARY=SALARY+10000 WHERE EMP_ID=:1'
USING V_EMP_ID(INDX);
EXCEPTION
WHEN OTHERS
THEN
FOR J IN 1 .. SQL%BULK_EXCEPTIONS.COUNT
LOOP
lv_error_string:=lv_error_string
||sqlerrm (-sql%bulk_exceptions(j).error_code)
|| ' for'||V_EMP_ID(INDX);
END LOOP;
END;
Use this: The error is that in exception block you are trying to access a loop variable that is being used in begin block.
So your || ' for'||V_EMP_ID(INDX); should be || ' for'||V_EMP_ID(J);
CREATE OR REPLACE PROCEDURE PROC1 (V_EMP_ID DBMS_SQL.NUMBER_TABLE)
IS
lv_error_string VARCHAR2(4000);
BEGIN
FORALL INDX IN V_EMP_ID.FIRST..V_EMP_ID.LAST SAVE EXCEPTIONS
EXECUTE IMMEDIATE 'UPDATE EMPLOYEES SET SALARY=SALARY+10000 WHERE EMP_ID=:1'
USING V_EMP_ID(INDX);
EXCEPTION
WHEN OTHERS
THEN
FOR J IN 1 .. SQL%BULK_EXCEPTIONS.COUNT
LOOP
lv_error_string:=lv_error_string
||sqlerrm (-sql%bulk_exceptions(j).error_code)
|| ' for'||V_EMP_ID(J);
END LOOP;
END;
Not sure why you use Execute Immediate when you can easily do as below:
CREATE OR REPLACE PROCEDURE PROC1 (V_EMP_ID DBMS_SQL.NUMBER_TABLE)
IS
lv_error_string VARCHAR2(4000);
BEGIN
FORALL INDX IN V_EMP_ID.FIRST..V_EMP_ID.LAST SAVE EXCEPTIONS
UPDATE EMPLOYEES
SET SALARY=SALARY+10000
WHERE EMP_ID= V_EMP_ID(INDX);
EXCEPTION
WHEN OTHERS
THEN
FOR J IN 1 .. SQL%BULK_EXCEPTIONS.COUNT
LOOP
lv_error_string:=lv_error_string
||sqlerrm (-sql%bulk_exceptions(j).error_code)
|| ' for'||V_EMP_ID(J);
END LOOP;
END;
I would suggest to go with a single DML statement. And yes DML error loggins is possible.Hope this helps
--Creating a error log table
BEGIN
DBMS_ERRLOG.create_error_log (dml_table_name => 'EMPLOYEES');
END;
/
--ERR$_EMPLOYEES --> Errro table created
--Insertion with erroreous record
UPDATE EMPLOYEES
SET SALARY = SALARY + 10000
where EMP_ID in (<EMP_ID COLLECTION array
OR simple EMP_IDs>) LOG ERRORS
INTO ERR$_EMPLOYEES ('UPDATE') REJECT LIMIT UNLIMITED;
--Error will be logged into ERR$_EMPLOYEES table

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 create a procedure in an oracle sql script and use it inside the script?

I want to create a script for my oracle DB, which drops tables. If the table does not exist, the script won't exit as fail, just print a text: "does not exists".
The script is the following:
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE mytable';
DBMS_Output.Put_Line(' table dropped');
EXCEPTION WHEN OTHERS THEN
IF SQLCODE = -942 THEN
DBMS_Output.Put_Line(' table not exists');
ELSE
DBMS_Output.Put_Line(' Unknown exception while dropping table');
RAISE;
END IF;
END;
I want to drop a lot of table in one script, and I don't want to write these lines more than once.
Is there any way, to write it to a procedure or function which gets a parameter (the name of the table), and call this procedure in that script?
Maybe something like this:
drop_table_procedure('mytableA');
drop_table_procedure('mytableB');
Or maybe a procedure, which gets an undefined size list (like in java: String ... table names):
drop_tables_procedure('mytableA','mytableB');
Please give me some examples.
Thanks!
Yes, you can declare a "temporary" procedure in an anonymous PL/SQL block:
DECLARE
PROCEDURE drop_if_exists(p_tablename VARCHAR)
IS
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE '||p_tablename;
DBMS_Output.Put_Line(' table dropped');
EXCEPTION WHEN OTHERS THEN
IF SQLCODE = -942 THEN
DBMS_Output.Put_Line(' table not exists');
ELSE
DBMS_Output.Put_Line(' Unknown exception while dropping table');
RAISE;
END IF;
END;
BEGIN
drop_if_exists('TABLE_1');
drop_if_exists('TABLE_2');
END;
/
in execute immediate you need add name of database object.
here's the script
create table t1 (col1 int);
create table t2 (col1 int);
create procedure drop_my_table(av_name varchar2)
as
begin
EXECUTE IMMEDIATE 'DROP TABLE '||av_name;
DBMS_Output.Put_Line(' table dropped');
EXCEPTION WHEN OTHERS THEN
IF SQLCODE = -942 THEN
DBMS_Output.Put_Line(' table not exists');
ELSE
DBMS_Output.Put_Line(' Unknown exception while dropping table');
RAISE;
END IF;
end drop_my_table;
declare
type array_t is varray(2) of varchar2(30);
atbls array_t := array_t('t1', 't2');
begin
for i in 1..atbls.count loop
drop_my_table(atbls(i));
end loop;
end;
You can use below one also
create or replace PROCEDURE drop_if_exists(p_tablename in VARCHAR)
IS
v_var1 number;
begin
select 1 into v_var1 from user_tables where table_name=upper(p_tablename);
if v_var1=1
then
EXECUTE IMMEDIATE 'DROP TABLE '||p_tablename;
DBMS_Output.Put_Line(' table dropped');
else
DBMS_Output.Put_Line(' table not exist');
end if;
exception
when others then
DBMS_Output.Put_Line(' Unknown exception while dropping table');
RAISE;
end;
Call procedure
begin
drop_if_exists('emp');
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;