Need to put TRY - CATCH in oracle stored procedure - sql

I have created SP for creating unique index on multiple tables.
Now i need to create Try-catch (exception handling) for this, like incase index not created this should be catch ....
For example i have 200 tables and only 1 tables is giving error, Then 199 tables should be created with index and catch log have that one error-ed tables name.
Please help .
DECLARE
CURSOR C_TABLE IS
SELECT INPUT_TABLE,HISTORY_TABLE FROM FUNCTIONS WHERE TARGET_SYS IN ('ABC','DEC') AND ACTIVE_FLAG='Y';
SQL_CREATE_INX VARCHAR2(200);
SQL_TABLE_NAME VARCHAR2(200);
BEGIN
-- INPUT TABLE
FOR I IN C_TABLE
LOOP
SQL_CREATE_INX:='CREATE UNIQUE INDEX CLM1.AUDIT_SUB_SITE_INX ON '||I.INPUT_TABLE||' (AUDIT_NBR , SUB_AUDIT_NBR , STATE) ';
SQL_TABLE_NAME:=I.INPUT_TABLE;
EXECUTE IMMEDIATE SQL_CREATE_INX;
DBMS_OUTPUT.PUT_LINE('INDEX DONE : '||SQL_TABLE_NAME);
END LOOP;
DBMS_OUTPUT.PUT_LINE('INDEXES CREATED FOR ALL INPUT TABLES');
-- OUTPUT TABLE
FOR H IN C_TABLE
LOOP
SQL_CREATE_INX:='CREATE UNIQUE INDEX CLM1.AUDIT_SUB_SITE_INX ON '||H.HISTORY_TABLE||' (AUDIT_NBR , SUB_AUDIT_NBR , STATE) ';
SQL_TABLE_NAME:=H.HISTORY_TABLE;
EXECUTE IMMEDIATE SQL_CREATE_INX;
DBMS_OUTPUT.PUT_LINE('INDEX DONE : '||SQL_TABLE_NAME);
END LOOP;
DBMS_OUTPUT.PUT_LINE('INDEXES CREATED FOR ALL OUTPUT TABLES');
END;

You need to enclose all EXEC IMMEDIATE into BEGIN .. END
FOR (.... )
LOOP
/*
* Other Statements
*/
BEGIN /* try */
EXECUTE IMMEDIATE SQL_CREATE_INX;
/* If it is success the below output happens */
DBMS_OUTPUT.PUT_LINE('INDEX DONE : '||SQL_TABLE_NAME);
EXCEPTION /*catch */
/* It is like catch(Exception e) All execeptions go here..*/
WHEN OTHERS THEN
/* Log your error message here.. SQLERRM has it..*/
DBMS_OUTPUT.PUT_LINE('DDL FAILED FOR '||SQL_TABLE_NAME||'::FAILED WITH ERROR::'||SQLERRM);
END;
/*
* Other Statements
*/
END LOOP;

In oracle handling of exceptions is like below
Declare
--declaration
BEGIN
--executable_section
EXCEPTION
WHEN exception_name1 THEN
[statements]
WHEN exception_name2 THEN
[statements]
WHEN exception_name_n THEN
[statements]
WHEN OTHERS THEN
[statements]
END
By editing your procedure with exception block is below
DECLARE
CURSOR C_TABLE IS
SELECT INPUT_TABLE,HISTORY_TABLE FROM FUNCTIONS WHERE TARGET_SYS IN ('ABC','DEC') AND ACTIVE_FLAG='Y';
var1 number;
var2 number;
SQL_CREATE_INX VARCHAR2(200);
SQL_TABLE_NAME VARCHAR2(200);
BEGIN
-- INPUT TABLE
FOR I IN C_TABLE
LOOP
select 1 into var1 from user_tables where table_name=upper(i.INPUT_TABLE)
if var1=1
then
SQL_CREATE_INX:='CREATE UNIQUE INDEX CLM1.AUDIT_SUB_SITE_INX ON '||I.INPUT_TABLE||' (AUDIT_NBR , SUB_AUDIT_NBR , STATE) ';
SQL_TABLE_NAME:=I.INPUT_TABLE;
EXECUTE IMMEDIATE SQL_CREATE_INX;
DBMS_OUTPUT.PUT_LINE('INDEX DONE : '||SQL_TABLE_NAME);
end if;
END LOOP;
DBMS_OUTPUT.PUT_LINE('INDEXES CREATED FOR ALL INPUT TABLES');
-- OUTPUT TABLE
FOR H IN C_TABLE
LOOP
select 1 into var2 from user_tables where table_name=upper(h.HISTORY_TABLE)
if var2=1
then
SQL_CREATE_INX:='CREATE UNIQUE INDEX CLM1.AUDIT_SUB_SITE_INX ON '||H.HISTORY_TABLE||' (AUDIT_NBR , SUB_AUDIT_NBR , STATE) ';
SQL_TABLE_NAME:=H.HISTORY_TABLE;
EXECUTE IMMEDIATE SQL_CREATE_INX;
DBMS_OUTPUT.PUT_LINE('INDEX DONE : '||SQL_TABLE_NAME);
end if;
END LOOP;
DBMS_OUTPUT.PUT_LINE('INDEXES CREATED FOR ALL OUTPUT TABLES');
exception
WHEN OTHERS THEN
Dbms_output.put_line(sqlerrm ||' error occured with this error code '||SQLCODE);
END;
Some Predefined exceptions in oracle and edit the plsql block with your exception name.

Related

How to write Execute immediate in cursor select Query

How to wite EXECUTE IMMEDIATE in the cursor select Query.
CREATE OR REPLACE PROCEDURE biq_attendee_report (in_from_date IN DATE)
IS
l_cur_query VARCHAR2 (5000) := 'SELECT * from table X where c1='|| in_from_date;
CURSOR cur_attendee_data
IS
EXECUTE IMMEDIATE l_cur_query;
TYPE rec_attendee_data IS TABLE OF cur_attendee_data%ROWTYPE
INDEX BY PLS_INTEGER;
l_cur_attendee_data rec_attendee_data;
BEGIN
OPEN cur_attendee_data;
LOOP
FETCH cur_attendee_data BULK COLLECT INTO l_cur_attendee_data;
EXIT WHEN l_cur_attendee_data.COUNT = 0;
DBMS_OUTPUT.put_line ('here in first insert');
lrec := return_attendee_report ();
out_attendee_tab :=
return_attendee_arr_result (return_attendee_report ());
out_attendee_tab.DELETE;
FOR i IN 1 .. l_cur_attendee_data.COUNT
LOOP
BEGIN
NULL;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('Error occurred : ' || SQLERRM);
END;
END LOOP;
END LOOP;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('HERE INSIIDE OTHERS' || SQLERRM);
END;
here I try to use initialize cursor outside begin block but it rises exception, how to write a dynamic query for the cursor,
cur_attendee_data is
EXECUTE IMMEDIATE l_cur_query;
Error(113,8): PLS-00103: Encountered the symbol "CUR_ATTENDEE_DATA" when expecting one of the following: := . ( # % ;
If you fetch all rows with BULK COLLECT INTO ... then you need only one loop, the second loop is useless.
The basic solution would be this one:
CREATE OR REPLACE PROCEDURE biq_attendee_report (in_from_date IN DATE) IS
l_cur_query VARCHAR2 (5000) := 'SELECT * from {table X} where c1=:d';
cur_attendee_data SYS_REFCURSOR;
TYPE rec_attendee_data IS TABLE OF {table X}%ROWTYPE;
l_cur_attendee_data rec_attendee_data;
BEGIN
OPEN cur_attendee_data FOR l_cur_query USING in_from_date;
FETCH cur_attendee_data BULK COLLECT INTO l_cur_attendee_data;
FOR i IN 1 .. l_cur_attendee_data.COUNT LOOP
-- do whatever you like to do with l_cur_attendee_data(i)
END LOOP;
CLOSE cur_attendee_data;
END;
However, I don't see any reason to make dynamic SQL. You can simply run
CREATE OR REPLACE PROCEDURE biq_attendee_report (in_from_date IN DATE) IS
cur_attendee_data SYS_REFCURSOR;
TYPE rec_attendee_data IS TABLE OF {table X}%ROWTYPE;
l_cur_attendee_data rec_attendee_data;
BEGIN
OPEN cur_attendee_data FOR SELECT * from {table X} where c1 = in_from_date;
FETCH cur_attendee_data BULK COLLECT INTO l_cur_attendee_data;
FOR i IN 1 .. l_cur_attendee_data.COUNT LOOP
-- do whatever you like to do with l_cur_attendee_data(i)
END LOOP;
CLOSE cur_attendee_data;
END;
You can use below code instead -
CREATE OR REPLACE PROCEDURE biq_attendee_report (in_from_date IN DATE)
IS
l_cur_query VARCHAR2 (100) := 'SELECT * from table X where c1=:in_from_date';
TYPE t_cur IS REF CURSOR;
cur_attendee_data t_cur
TYPE rec_attendee_data IS TABLE OF cur_attendee_data%ROWTYPE
INDEX BY PLS_INTEGER;
l_cur_attendee_data rec_attendee_data;
BEGIN
OPEN cur_attendee_data FOR l_cur_query USING in_from_date;
LOOP
FETCH cur_attendee_data BULK COLLECT INTO l_cur_attendee_data;
EXIT WHEN l_cur_attendee_data.COUNT = 0;
DBMS_OUTPUT.put_line ('here in first insert');
lrec := return_attendee_report ();
out_attendee_tab :=
return_attendee_arr_result (return_attendee_report ());
out_attendee_tab.DELETE;
FOR i IN 1 .. l_cur_attendee_data.COUNT
LOOP
BEGIN
NULL;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('Error occurred : ' || SQLERRM);
END;
END LOOP;
END LOOP;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('HERE INSIIDE OTHERS' || SQLERRM);
END;
You need to use open cursor for '' as following:
CREATE OR REPLACE PROCEDURE biq_attendee_report (in_from_date IN DATE)
IS
l_cur_query VARCHAR2 (5000) := 'SELECT * from table X where c1='|| in_from_date;
cur_attendee_data SYS_REFCURSOR; -- JUST DECLARED THE CURSOR
--TYPE rec_attendee_data IS TABLE OF cur_attendee_data%ROWTYPE
-- INDEX BY PLS_INTEGER; -- this declaration must be at schema level
l_cur_attendee_data rec_attendee_data;
BEGIN
OPEN cur_attendee_data for l_cur_query; -- OPEN THE CURSOR WITH DYNAMIC QUERY
..
.. -- YOUR CODE AS IT IS
..
Cheers!!

Implicit cursor and NO_DATA_FOUND exception

I have the following PL/SQL code:
BEGIN
FOR c IN (SELECT ...) LOOP
<code1>;
END LOOP;
<code2>;
EXCEPTION WHEN NO_DATA_FOUND THEN
NULL;
END;
This code should run code1 multiple times within a loop and upon finishing this loop code2 should be executed. Otherwise if SELECT query does not find data then I expect this should raise an exception and overstep code2, but this is not happening. Why?
NO_DATA_FOUND is thrown by statements that must return exactly one row but do not find a matching row, e.g.
DECLARE x NUMBER;
BEGIN
SELECT foo INTO x FROM bar WHERE xyz='abc';
EXCEPTION
WHEN NO_DATA_FOUND THEN
...
END;
In your case, you could do the following:
DECLARE foundSomething BOOLEAN := FALSE;
BEGIN
FOR c IN (SELECT ...) LOOP
foundSomething := TRUE;
<code1>;
END LOOP;
IF NOT foundSomething THEN
NULL; -- handle the situation
ELSE
<code2>;
END IF;
END;
No, that's not what is supposed to happen.
If there is no data, then loop runs 0 times - i.e. it skips code1 and executes code2.
You can define explicit cursors and do the checks for data unavailability like this:
DECLARE
cursor cur is select 1 a from dual where 1 = 1;
type tab is table of cur%rowtype;
v tab;
BEGIN
open cur;
loop
fetch cur bulk collect into v;
if v.count = 0 then
raise no_data_found;
end if;
dbms_output.put_line('Code1');
end loop;
close cur;
dbms_output.put_line('Code2');
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line('Error');
END;
/
You can easily extends this code to do other things such as divide fetch into batches etc.

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

Simple PL/SQL to check if table exists is not working

I'm in the process of converting some stored procedures from Sybase TSQL to Oracle PL/SQL and I've already come across a problem which I'm struggling to resolve!
The below code will not run:
DECLARE
t INT := 0;
t_error EXCEPTION;
v_line VARCHAR2(100);
BEGIN
SELECT COUNT(*) INTO t FROM user_tables WHERE table_name = UPPER('tbl_BSUK_PriceIssue');
IF t = 1 THEN
EXECUTE IMMEDIATE 'DROP TABLE tbl_BSUK_PriceIssue';
t := 0;
SELECT COUNT(*) INTO t FROM user_tables WHERE table_name = UPPER('tbl_BSUK_PriceIssue');
IF t = 1 THEN
RAISE t_error;
END IF;
END IF;
EXCEPTION
WHEN t_error THEN
v_line := '<<< FAILED DROPPING table tbl_BSUK_PriceIssue >>>';
dbms_output.put_line (v_line);
WHEN OTHERS THEN
v_line := '<<< Unknown Error >>>';
dbms_output.put_line (v_line);
END;
END;
I get the following error message, what am I doing wrong?!
Error starting at line : 17 in command - DECLARE
t INT := 0; t_error EXCEPTION; v_line VARCHAR2(100);
BEGIN
SELECT COUNT(*) INTO t FROM user_tables WHERE table_name =
UPPER('tbl_BSUK_PriceIssue');
IF t = 1 THEN EXECUTE IMMEDIATE 'DROP TABLE tbl_BSUK_PriceIssue';
t := 0; SELECT COUNT(*) INTO t FROM user_tables WHERE table_name =
UPPER('tbl_BSUK_PriceIssue'); IF t = 1 THEN
RAISE t_error; END IF; END IF;
EXCEPTION WHEN t_error THEN v_line := '<<< FAILED DROPPING table
tbl_BSUK_PriceIssue >>>'; dbms_output.put_line (v_line); WHEN
OTHERS THEN
v_line := '<<< Unknown Error >>>';
dbms_output.put_line (v_line); END;
END; Error report - ORA-06550: line 30, column 1: PLS-00103: Encountered the symbol "END"
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
I'm actually trying to replace the following TSQL with a PL/SQL version:
-- Create temp table for relevant trev_id's
IF OBJECT_ID('dbo.tbl_BSUK_PriceIssue') IS NOT NULL
BEGIN
DROP TABLE dbo.tbl_BSUK_PriceIssue
IF OBJECT_ID('dbo.tbl_BSUK_PriceIssue') IS NOT NULL
PRINT '<<< FAILED DROPPING TABLE dbo.tbl_BSUK_PriceIssue >>>'
ELSE
PRINT '<<< DROPPED TABLE dbo.tbl_BSUK_PriceIssue >>>'
END
go
try to remove END; in this section
WHEN OTHERS THEN
v_line := '<<< Unknown Error >>>';
dbms_output.put_line (v_line);
END;
UPD. Actually, you can do it a bit shorter, no need to check if table exists after drop
declare
eTableNotExists exception;
pragma exception_init(eTableNotExists, -00942);
begin
EXECUTE IMMEDIATE 'DROP TABLE tbl_BSUK_PriceIssue';
dbms_output.put_line('<<< DROPPED TABLE dbo.tbl_BSUK_PriceIssue >>>');
exception
when eTableNotExists then null
when others then
dbms_output.put_line ('<<< Unknown Error >>>' || sqlerrm);
end;
/
I don't know about the error, but you can do what you want in a fraction of the code. You don't need the count variable if you use EXISTS() and you don't need EXECUTE IMMEDIATE because you don't have variable data in the command:
IF 1 = (SELECT 1 FROM user_tables WHERE table_name = 'TBL_BSUK_PRICEISSUE') THEN
DROP TABLE tbl_BSUK_PriceIssue;
IF 1 = (SELECT 1 FROM user_tables WHERE table_name = 'TBL_BSUK_PRICEISSUE') THEN
RAISE EXCEPTION;
END IF;
END IF;

how handle table or view does not exist exception?

I have a set of table names, let say 150. Each table have mail_id column, now I want to search one mail_id in all of the table. For that I wrote one Plsql block. When I loop through the set of table some tables do not exists so it raises an exception. I have exception handling block to handle that exception. Now I want to loop entire table even though it raise an exception? Any idea? Actually my block didn't handle that particular exception!
declare
my_mail_id varchar2(50):='xyaksj#jsm.com';
tmp_table varchar2(125);
type varchar_collector is table of varchar2(255);
var varchar_collector;
table_does_not_exist exception;
PRAGMA EXCEPTION_INIT(table_does_not_exist, -00942);
begin
for cntr in (select table_name from user_tables)
loop
tmp_table:=cntr.table_name;
dbms_output.put_line(tmp_table);
for mail in (select email_address from tmp_table where lower(email_address) like '%my_mail_id%' )
loop
dbms_output.put_line(tmp_table);
end loop;
end loop;
exception
when no_data_found then
dbms_output.put_line('email address not found');
WHEN table_does_not_exist then
dbms_output.put_line('table dose not exists');
WHEN OTHERS THEN
--raise_application_error(-20101, 'Expecting at least 1000 tables');
IF (SQLCODE = -942) THEN
--DBMS_Output.Put_Line (SQLERRM);
DBMS_Output.Put_Line ('in exception');--this exception not handled
ELSE
RAISE;
END IF;
end;
Just handle your exceptions in anonymous block inside the loop.
DECLARE
my_mail_id VARCHAR2(50) := 'xyaksj#jsm.com';
tmp_table VARCHAR2(125);
TYPE varchar_collector IS TABLE OF VARCHAR2(255);
var varchar_collector;
table_does_not_exist EXCEPTION;
PRAGMA EXCEPTION_INIT(table_does_not_exist, -00942);
BEGIN
FOR cntr IN (SELECT table_name FROM user_tables)
LOOP
BEGIN
tmp_table := cntr.table_name;
dbms_output.put_line(tmp_table);
FOR mail IN (SELECT email_address
FROM tmp_table
WHERE lower(email_address) LIKE '%my_mail_id%')
LOOP
dbms_output.put_line(tmp_table);
END LOOP;
EXCEPTION
WHEN no_data_found THEN
dbms_output.put_line('email address not found');
WHEN table_does_not_exist THEN
dbms_output.put_line('table dose not exists');
WHEN OTHERS THEN
--raise_application_error(-20101, 'Expecting at least 1000 tables');
IF (SQLCODE = -942)
THEN
--DBMS_Output.Put_Line (SQLERRM);
DBMS_Output.Put_Line('in exception'); --this exception not handled
ELSE
RAISE;
END IF;
END;
END LOOP;
END;
If you're selecting from user_tables and finding that some of them do not exist then you're probably trying to query tables that are in the recycle bin (their names begin BIN$).
If so, change your query to:
select table_name
from user_tables
where dropped = 'NO';
You should replace your second cursor with a call to execute immediate also, constructing the query by concatenating in the table_name not just using a variable as the table name, and you might as well construct the query as:
select count(*)
from table_name
where lower(email_address) like '%my_mail_id%'
and rownum = 1;
That way you'll retrieve a single record that is either 0 or 1 to indicate whether the email address was found, and no need for error handling.
try below code...
DECLARE
foo BOOLEAN;
BEGIN
FOR i IN 1..10 LOOP
IF foo THEN
GOTO end_loop;
END IF;
<<end_loop>> -- not allowed unless an executable statement follows
NULL; -- add NULL statement to avoid error
END LOOP; -- raises an error without the previous NULL
END;