Finding exception (PL/SQL) - oracle

I am running a code in Oracle that asks the user to input a route type, and then the program fetches the longest and shortest routes depending on the user-input route, as well as the number of rows fetched. I need to add any exception to the code and I am trying to think of what kind of exception I could add in this case. I was thinking of adding an exception that gives an error if the rows fetched is <1. However, I am not sure if such exception exists. I also need to add an exception for an invalid route type. I have represented this as an IF statement. Not sure if this counts as an exception.
My code is:
SET SERVEROUTPUT ON;
SET VERIFY OFF
DECLARE
v_type VARCHAR2 (20);
min_length NUMBER;
max_length NUMBER;
v_count NUMBER;
BEGIN
v_type := '&InsertTypeRoute';
IF v_type = 'Multi-Lane Divided' OR
v_type = 'Paved Divided' OR
v_type = 'Paved Undivided' THEN
SELECT MIN(LENGTH_KM), MAX(LENGTH_KM), COUNT(LENGTH_KM) INTO min_length, max_length, v_count
FROM TBLROUTE WHERE TYPE = v_type;
DBMS_OUTPUT.PUT_LINE('The minimum length is: ' || TO_CHAR(min_length));
DBMS_OUTPUT.PUT_LINE('The maximum length is: ' || TO_CHAR(max_length));
DBMS_OUTPUT.PUT_LINE ('Total number of '|| v_type ||' route is: ' || TO_CHAR(v_count));
ELSE
DBMS_OUTPUT.PUT_LINE ('Route type cannot be found');
END IF;
/*
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE ('Route type cannot be found'); */
END;
/
How about these exception? Are they appropriate in this case?
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE ('Data not found');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('This program encountered an error');

It is suggested to add SQLCODE and SQLERRM if you are using WHEN_OTHERS along with RAISE
This will tell you exactly what went wrong.
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('ERROR MESSAGE :' || SQLCODE || ' :' || SQLERRM );
RAISE;

"Are they appropriate in this case?"
Handling exceptions like this is universally accepted as bad practice. It swallows the real exception, so the user knows that something failed but they have absolutely no idea what.
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('This program encountered an error');
Here is a circumstance where doing nothing is better than doing that.
In your situation I suggest you rely on the default PL/SQL NO_DATA_FOUND exception to handle no records, and raise a bespoke exception for when there's only one. Something like this:
DECLARE
v_type VARCHAR2 (20);
min_length NUMBER;
max_length NUMBER;
v_count NUMBER;
-- user-defined exception
x_single_route exception;
pragme expcetion_init(x_single_route, -20001);
BEGIN
v_type := '&InsertTypeRoute';
IF v_type = 'Multi-Lane Divided' OR
v_type = 'Paved Divided' OR
v_type = 'Paved Undivided'
THEN
SELECT MIN(LENGTH_KM), MAX(LENGTH_KM), COUNT(LENGTH_KM) INTO min_length, max_length, v_count
FROM TBLROUTE WHERE TYPE = v_type;
IF v_count > 1 THEN
DBMS_OUTPUT.PUT_LINE('The minimum length is: ' || TO_CHAR(min_length));
DBMS_OUTPUT.PUT_LINE('The maximum length is: ' || TO_CHAR(max_length));
DBMS_OUTPUT.PUT_LINE ('Total number of '|| v_type ||' route is: ' || TO_CHAR(v_count));
ELSE
raise x_single_route;
END IF;
EXCEPTION
WHEN x_single_route THEN
DBMS_OUTPUT.PUT_LINE ('Route has only one leg');
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE ('Route type cannot be found');
END;
/
Generally it is good practice to RAISE exceptions: users want to know when something goes wrong. (Although as this appears to be a user-facing program, so it's slightly different.)
...
EXCEPTION
WHEN x_single_route THEN
DBMS_OUTPUT.PUT_LINE ('Route has only one leg');
RAISE;
WHEN NO_DATA_FOUND THEN
....

You could use the RAISE_APPLICATION_ERROR procedure to raise a particular exception in the user-defined range (-20000..-20999), then define an exception so you can catch it. Something like the following should work:
DECLARE
SOMETHING_UNWANTED_HAPPENS BOOLEAN := TRUE;
BEGIN
IF SOMETHING_UNWANTED_HAPPENS THEN
RAISE_APPLICATION_ERROR(-20001, 'Something I didn''t want happened');
END IF;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -20001 THEN
DBMS_OUTPUT.PUT_LINE('My user-defined exception was raised');
-- Do whatever else you want to do to handle this exception
ELSE
DBMS_OUTPUT.PUT_LINE('Something else happened');
END IF;
END;
Or you could declare an EXCEPTION variable, initialize it with PRAGMA EXCEPTION_INIT, and then use it like any other exception:
DECLARE
my_exception EXCEPTION;
PRAGMA EXCEPTION_INIT(my_exception, -20001);
SOMETHING_UNWANTED_HAPPENS BOOLEAN := TRUE;
BEGIN
IF SOMETHING_UNWANTED_HAPPENS THEN
RAISE my_exception;
END IF;
EXCEPTION
WHEN my_exception THEN
DBMS_OUTPUT.PUT_LINE('My user-defined exception was raised');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Something else happened');
END;

Related

Calling user defined exception but instead when others than exception is raising

I have a anonymous block like below
DECLARE
exc1 EXCEPTION;
i integer:='1';
BEGIN
BEGIN
IF i = 1 THEN
RAISE exc1;
END IF;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20481,
'Error while inserting/update into Table- ' ||
SQLERRM);
END;
EXCEPTION
WHEN exc1 THEN
raise_application_error(-20001, 'test123');
END;
I just want to raise the exc1 exception. But here instead when others then exception is raising. I specifically raise the exception exc1 in if condition block so it has to call right?
The best option here is to have just one exception block:
DECLARE
exc1 EXCEPTION;
i integer:='1';
BEGIN
IF i = 1 THEN
RAISE exc1;
END IF;
EXCEPTION
WHEN exc1 THEN
raise_application_error(-20001, 'test123');
WHEN OTHERS THEN
raise_application_error(-20481,
'Error while inserting/update into Table- ' ||
SQLERRM);
END;
/
But if you really need nested anonymous block with own exceptions blocks, you need to add one more exception handler:
DECLARE
exc1 EXCEPTION;
i integer:='1';
BEGIN
BEGIN
IF i = 1 THEN
RAISE exc1;
END IF;
EXCEPTION
WHEN exc1 THEN
raise;
WHEN OTHERS THEN
raise_application_error(-20481,
'Error while inserting/update into Table- ' ||
SQLERRM);
END;
EXCEPTION
WHEN exc1 THEN
raise_application_error(-20001, 'test123');
END;
/
PS. I'd suggest you do not use when others then without adding original exception into the error stack, ie 3rd parameter of raise_application_error() should be true:
DECLARE
exc1 EXCEPTION;
i integer:='1';
BEGIN
IF i = 1 THEN
RAISE exc1;
END IF;
EXCEPTION
WHEN exc1 THEN
raise_application_error(-20001, 'test123', true);
WHEN OTHERS THEN
raise_application_error(-20481,
'Error while inserting/update into Table- ' ||
SQLERRM
, true);
END;
/

PL/SQL Continue exection after error handled

I'm newbie with PL/SQL and i've got the following situation.
In the following scritp i want that the script continue iterating if they found any exception. The exception are handled but I cannot execute the continue statement out of a loop.
declare
l_max_ID number;
l_Temp_val number;
type array_t is varray(135) of varchar2(30);
arrayTable array_t := array_t('YSXQTAAA', 'YSXQTAFA', ... );
array array_t := array_t('YSXQNAAA', 'YSXQNAFA', ...);
begin
dbms_output.put_line(arrayTable.count);
for i in 1..arrayTable.count loop
dbms_output.put_line('Tabla impactada ' || arrayTable(i));
execute immediate 'select max(id)+1 from ' || arrayTable(i) into l_max_ID;
execute immediate 'alter sequence ' || array(i) || ' restart start with ' || TO_CHAR(l_Max_ID);
dbms_output.put_line('alter sequence RS1.' || array(i) || ' restart start with ' || TO_CHAR(l_Max_ID));
end loop;
rollback;
EXCEPTION
WHEN NO_DATA_FOUND THEN
/* HAndle an error that gets raised when a query returns nothing */
dbms_output.put_line('Error 1');
WHEN TOO_MANY_ROWS THEN
/* HAndle the situation when too much data is returned such as with a select-into */
dbms_output.put_line('Error 2');
WHEN OTHERS THEN
dbms_output.put_line('Error 3');
end;
The problem is that some tables doesn't have id column and the select throw an exception. The array contains more than a hundred elements and delete each one takes a lot of time.
Put the exception handling block inside the loop:
begin
dbms_output.put_line(arrayTable.count);
for i in 1..arrayTable.count loop
begin
dbms_output.put_line('Tabla impactada ' || arrayTable(i));
execute immediate 'select max(id)+1 from ' || arrayTable(i) into l_max_ID;
execute immediate 'alter sequence ' || array(i) || ' restart start with ' || TO_CHAR(l_Max_ID);
dbms_output.put_line('alter sequence RS1.' || array(i) || ' restart start with ' || TO_CHAR(l_Max_ID));
EXCEPTION
WHEN NO_DATA_FOUND THEN
/* HAndle an error that gets raised when a query returns nothing */
dbms_output.put_line('Error 1');
continue;
WHEN TOO_MANY_ROWS THEN
/* HAndle the situation when too much data is returned such as with a select-into */
dbms_output.put_line('Error 2');
continue;
WHEN OTHERS THEN
dbms_output.put_line('Error 3');
continue;
end;
end loop;
rollback;
end;

Trying to get the actual data that cause an exception

I have a procedure that takes an input of 2 Associative arrays and after some basic count checks, does a FORALL statement to insert the data into a table.
Here is the procedure:
PROCEDURE INSERT_RECS(P_PROD_TYP IN prod_type, P_PROD_ADD_PK IN prod_pk_type)
IS
uniq_key EXCEPTION;
PRAGMA EXCEPTION_INIT(uniq_key, -00001);
loc_cnt NUMBER;
BEGIN
IF P_PROD_TYP.COUNT = P_PROD_ADD_PK.COUNT THEN
FORALL i IN P_PROD_TYP.FIRST .. P_PROD_TYP.LAST
INSERT INTO product_table ( pk,
id,
created_by,
created_on,
last_chg_by,
last_chg_on)
VALUES (P_PROD_ADD_PK(i),
P_PROD_TYP(i).id,
P_PROD_TYP(i).created_by,
P_PROD_TYP(i).created_on,
NULL,
NULL);
END IF;
EXCEPTION
WHEN uniq_key THEN
loc_cnt := SQL%BULK_EXCEPTIONS.count;
FOR i IN 1 .. loc_cnt LOOP
dbms_output.put_line('EXCEPTION: Array Index: ' || SQL%BULK_EXCEPTIONS(i).error_index ||
' Message: ' || SQLERRM(-SQL%BULK_EXCEPTIONS(i).ERROR_CODE) ||
' SQLERRM: ' || SQLERRM ||
' SQLCODE: ' || SQLCODE ||
' stack: ' || SYS.dbms_utility.format_call_stack);
END LOOP;
RETURN;
END;
What I would like is if I hit an exception, is there a way that I could have view of the record that is causing the issue, essentially the index in the associative array or at least have the SQL% info have the info.
I have look at the following:
http://www.dba-oracle.com/plsql/t_plsql_exceptions.htm
but this outputs the info about the column but that is not what I am after.
Not sure if really answers your query but you can use the loop variable i in exception block to display the content of the exception array in your case. See below an example procedure:
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
---trying to rasie an exception by using a calculation
SET SALARY=SALARY * 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
WHERE ID_E= V_EMP_ID(INDX);
EXCEPTION
WHEN OTHERS
THEN
FOR i IN 1 .. SQL%BULK_EXCEPTIONS.COUNT
LOOP
---Am printing the value of the exception array.
dbms_output.put_line('exception Raised for record' ||V_EMP_ID(i));
END LOOP;
END;
/
Ouput:
SQL> DECLARE
empid DBMS_SQL.NUMBER_TABLE;
BEGIN
empid (1) := 1;
empid (2) := 9;
PROC1 (empid);
END;
/
exception Raised for record 1
PL/SQL procedure successfully completed.

need to modify the below oracle query

I have the below script , i want to modify it such a way lets say if it is executed first time then it will create the column but lets say if it is executed second time then it will show fail message which is not correct it should show the message that column is created and also if there comes any exception lets say column i s not created due to some technical exception then it should show fail message , please advise how to achieve this
SELECT COUNT(*) INTO L_COL_EXISTS FROM USER_TAB_COLS WHERE COLUMN_NAME = 'TOR' and TABLE_NAME='AVOICE';
IF L_COL_EXISTS = 1
THEN
outcome := 'Success';
ELSE
outcome := 'Fail';
END IF;
DBMS_OUTPUT.PUT_LINE(outcome);
folks please advise
I suppose it can help you
CREATE OR REPLACE PROCEDURE add_column
(
v_table_name IN VARCHAR2,
v_column_name IN VARCHAR2,
v_column_type IN VARCHAR2
)
IS
v_column_exists pls_integer;
v_ddl_str varchar2(1024);
BEGIN
BEGIN
SELECT count(*)
INTO v_column_exists
FROM user_tab_columns
WHERE table_name = upper(v_table_name)
and column_name = upper(v_column_name);
EXCEPTION
WHEN OTHERS THEN
RAISE;
END;
if v_column_exists = 0 then
v_ddl_str := 'alter table ' || v_table_name || ' add ( ' || v_column_name || ' ' || v_column_type || ')';
BEGIN
EXECUTE IMMEDIATE alter_str;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line ('something wrong');
RAISE;
END;
ELSE
dbms_output.put_line ('Column exists');
END IF;
EXCEPTION
WHEN OTHERS THEN
RAISE;
END add_column;
/

Stored procedure exception handling

SQL> DECLARE
2 TotalUpd NUMBER(36) := 0;
3 BEGIN
4 dbms_output.put_line ('Job Start time............... : ' || to_char(SYSDATE, ' hh24:mi:ss'));
5 UPDATE Asset SET _status = 'PROGRESS' WHERE status is null;
6 TotalUpd := SQL%ROWCOUNT;
7 dbms_output.put_line('Total Records Updated. : ' || TotalUpd);
8 COMMIT;
9 EXCEPTION
10 WHEN NO_DATA_FOUND THEN
11 dbms_output.put_line ('No more data to update.');
12 WHEN OTHERS THEN
13 dbms_output.put_line ('Error while status as SUCCESS ');
14 END ;
15 /
The result for the above procedure is
Job Start time............... : 04:41:41
Total Records Updated. : 0
But my expected result is "No more row to be updated" must be printed,since i have truncated the table Asset.Please tell where I went wrong in this.
The NO_DATA_FOUND error is not thrown in update statements.
It is thrown in select into statements if the select statement would return nothing.
See also Tahiti on select into under select_item: *If the SELECT INTO statement returns no rows, PL/SQL raises the predefined exception NO_DATA_FOUND.*
Oracle does not consider it an exception if an update statement does not update anything, hence no exception is thrown. However, if a select into statement cannot fill the variables, it is considered an error (and therefore in this case the NO_DATA_EXCEPTION is thrown(
it is as simple as the update does not geneate an error if there is no data.
you need to look at the value of TotalUpd if you want to control the flow of your code
DECLARE
TotalUpd NUMBER(36) := 0;
BEGIN
dbms_output.put_line ('Job Start time............... : '
|| TO_CHAR(SYSDATE, ' hh24:mi:ss'));
UPDATE Asset SET _status = 'PROGRESS' WHERE status IS null;
TotalUpd := SQL%ROWCOUNT;
IF TotalUpd = 0 THEN
dbms_output.put_line ('No more data to update.');
ELSE
dbms_output.put_line('Total Records Updated. : '
|| TotalUpd);
END IF;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line ('Error while status as SUCCESS ');
END;
NO_DATA_FOUND is thrown if a select into does not return any row, not if no rows were updated after an update statement.
I suggest you move your logic for handling this exception after the update itself:
IF (SQL%ROWCOUNT = 0) THEN
dbms_output.put_line ('No more data to update.');
Think NO_DATA_FOUND exception is only raised by a SELECT statement which you aren't using. Try testing SQL%COUNT and output as required.

Resources