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.
Related
I am using cursors to insert data in a table because if a record fails I only want that record to be discarded and keep inserting the rest.
So I am using a cursor to retrieve the information.
Is there any way to insert all columns of a cursor at once and not selecting them one by one?
cursor c1 is
select a,b,c,d,e from ab where a = 'something';
begin
for var_c1 in c1 loop
begin
insert into ba (a,b,c,d,e)
values (var_c1.all);
-- instead of values (var_c1.a, var_c1.b, var_c1.c,var_c1.d, var_c1.e)
exception when others then continue;
end;
end;
For performance you should put all your records into a collection, then you can use BULK INSERT... SAVE EXCEPTION like below:
DECLARE
TYPE t_tab IS TABLE OF ba%ROWTYPE;
l_tab t_tab := t_tab();
l_error_count NUMBER;
ex_dml_errors EXCEPTION;
PRAGMA EXCEPTION_INIT(ex_dml_errors, -24381);
BEGIN
-- Fill the collection. ***
-- l_tab <--- ??
-- *** next question is to make l_tab fill with the result of your cursor
--
-- Perform a bulk operation.
BEGIN
FORALL i IN l_tab.first .. l_tab.last SAVE EXCEPTIONS
INSERT INTO ba
VALUES l_tab(i);
EXCEPTION
WHEN ex_dml_errors THEN
l_error_count := SQL%BULK_EXCEPTIONS.count;
DBMS_OUTPUT.put_line('Number of failures: ' || l_error_count);
FOR i IN 1 .. l_error_count LOOP
DBMS_OUTPUT.put_line('Error: ' || i ||
' Array Index: ' || SQL%BULK_EXCEPTIONS(i).error_index ||
' Message: ' || SQLERRM(-SQL%BULK_EXCEPTIONS(i).ERROR_CODE));
END LOOP;
END;
END;
/
Hope it helps
I have below stored procedure. It works fine. But I wanted to test it for error scenarios. Even when there is an error, the procedure executes successfully without showing the error message. When I enable the set serveroutput on, it shows the error message. But I want to capture the error message.
create or replace PROCEDURE COMP_JSON (
OUT_MESSAGE OUT VARCHAR2,
PNI_ID IN NUMBER
)
AS
CURSOR C1 IS SELECT 1 AS ID,TYPE_ID, COMP, TYPE, PREV_AMOUNT, CURR_AMOUNT FROM V_COMP_COST;
SECID VARCHAR2(100);
K NUMBER:= 0;
L NUMBER:= 1000;--Commit Interval
LRETVALUE VARCHAR2(200):='0';
V_TYPE_ID JSON_DATA.TYPE_ID%TYPE;
V_COMP JSON_DATA.COMP%TYPE;
V_TYPE JSON_DATA.TYPE%TYPE;
BEGIN
APEX_JSON.INITIALIZE_CLOB_OUTPUT;
/* Cost Comparison */
IF NVL(PNI_ID, 1) = 1
THEN
K := 0;
BEGIN
FOR I IN C1
LOOP
V_TYPE_ID := I.TYPE_ID;
V_COMP := I.COMP;
V_TYPE := I.TYPE;
APEX_JSON.OPEN_OBJECT;
APEX_JSON.WRITE('prevAmt',I.PREV_AMOUNT);
APEX_JSON.WRITE('currAmt',I.CURR_AMOUNT);
APEX_JSON.CLOSE_OBJECT;
INSERT INTO JSON_DATA
VALUES (I.ID,I.TYPE_ID,I.COMP,I.TYPE,APEX_JSON.GET_CLOB_OUTPUT);
/* Commit Interval */
K := K+1;
IF MOD(K,L) = 0
THEN
COMMIT;
END IF;
APEX_JSON.FREE_OUTPUT;
IF K > 5
THEN
RAISE_APPLICATION_ERROR(-20000, NULL);
END IF;
END LOOP;
EXCEPTION
WHEN OTHERS
THEN LRETVALUE := '-1,k:Problem in loading Data -' || SQLERRM || ' AT: [' || V_TYPE_ID || '] [' || V_COMP || '] [' || V_TYPE || ']';
END;
COMMIT;
IF LRETVALUE <> '0'
THEN
OUT_MESSAGE := LRETVALUE;
RETURN;
END IF;
END IF;
EXCEPTION
WHEN OTHERS
THEN DBMS_OUTPUT.PUT_LINE('ERROR MESSAGE' || SQLERRM);
END COMP_JSON;
You're using a nested block to throw your exception, but it will continue processing. The outbound variable "OUT_MESSAGE" should capture that value. Is it? if so, you can see what it is with this:
SQL> VAR ERR_MSG VARCHAR2;
SQL> EXEC COMP_JSON(:ERR_MSG, 5); --whatever you use for PNI_ID....
PL/SQL procedure successfully completed.
SQL> PRINT ERR_MSG;
If your program never throws an error, then OUT_MESSAGE will never be set, thus it will be null upon completion of the program.
If you are looking to throw an error from your program if your "nested block" throws an error, then you need to re-raise the exception so that the outer exception catches it:
IF K > 5
THEN
RAISE_APPLICATION_ERROR(-20000, NULL);
END IF;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
LRETVALUE := '-1,k:Problem in loading Data -' || SQLERRM || ' AT: [' || V_TYPE_`ID || '] [' || V_COMP || '] [' || V_TYPE || ']';
RAISE_APPLICATION_ERROR(-20000, LRETVALUE );
END;
If you need the caller to raise an exception, you need that your procedure propagates it to the external.
For example:
SQL> create or replace procedure raiseException(p in number) is
2 n number;
3 begin
4 n := p/0;
5 exception
6 when others then
7 dbms_output.put_line('Error message: ' || sqlerrm);
8 raise;
9 end;
10 /
Procedure created.
SQL> create or replace procedure procedureCall is
2 begin
3 raiseException(10);
4 end;
5 /
Procedure created.
The first procedure will print a message and raise an exception; in this way, we have the error message in output and an exception:
SQL> exec procedureCall
Error message: ORA-01476: divisor is equal to zero
BEGIN procedureCall; END;
*
ERROR at line 1:
ORA-01476: divisor is equal to zero
ORA-06512: at "ALEK.RAISEEXCEPTION", line 8
ORA-06512: at "ALEK.PROCEDURECALL", line 3
ORA-06512: at line 1
If you remove the RAISE, the exception will be handled and not propagated, thus giving no error:
SQL> create or replace procedure raiseException(p in number) is
2 n number;
3 begin
4 n := p/0;
5 exception
6 when others then
7 dbms_output.put_line('Error message: ' || sqlerrm);
8 end;
9 /
Procedure created.
SQL> exec procedureCall
Error message: ORA-01476: divisor is equal to zero
PL/SQL procedure successfully completed.
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;
/
I have the following trigger
CREATE OR REPLACE TRIGGER L_BIUR_G_LAY
BEFORE INSERT OR UPDATE ON G_LAY
FOR EACH ROW
When (new.g_roo is not null)
DECLARE
x number(1);
stmt varchar(255);
BEGIN
FOR I IN (SELECT DISTINCT G_TAB FROM G_LEG)
LOOP
stmt := 'select distinct g_lind from ' || i.g_tab || ' where g_roo = ' || :new.g_roo;
Execute immediate stmt into x;
IF (x<>0) THEN
RAISE_APPLICATION_ERROR(-2001, 'G_ROO cannot be inserted where G_LIND IS NOT ZERO');
END IF;
END LOOP;
END;
/
and when I do
insert into G_LAY (G_OGCS, G_OGC, G_ROO, G_NM, G_TI, G_AB, G_DATE)
(select G_LAY_SEQ.NEXTVAL, 1, G_ROO, G_LGNDIT, G_UNE, 'Pipe Data Long - ' || G_UR, sysdate
from G_DTABLE where G_LIND = 0);
I get the following error
Error report:
SQL Error: ORA-01403: no data found
ORA-06512: at "L_BIUR_G_LAY", line 10
ORA-04088: error during execution of trigger 'G_LAY'
01403. 00000 - "no data found"
Any help would be greatly appreciated. It tried PRAGMA AUTONOMOUS_TRANSACTION after Declare in my trigger but it didn't help me
The problem is not with your INSERT statement. The problem occurs when the dynamically-built SELECT statement in the trigger is executed - no data is found so an ORA-01403 is raised. It looks to me like the easiest thing to do would be to do a SELECT COUNT(...):
CREATE OR REPLACE TRIGGER L_BIUR_G_LAY
BEFORE INSERT OR UPDATE ON G_LAY
FOR EACH ROW
When (new.g_rowno is not null)
DECLARE
x number;
stmt varchar(255);
BEGIN
FOR I IN (SELECT DISTINCT G_DISPCNTRLTAB FROM G_LEG) LOOP
stmt := 'select COUNT(*) from ' || i.g_dispcntrltab ||
' where g_rowno = ' || :new.g_rowno ||
' and g_lind <> 0';
Execute immediate stmt into x;
IF x <> 0 THEN
RAISE_APPLICATION_ERROR(-20001, 'G_RWNO cannot be inserted where G_LIND IS NOT ZERO');
END IF;
END LOOP;
END L_BIUR_G_LAY;
I also changed the error number which is raised as -2001 is not in the range of errors which can be raised using RAISE_APPLICATION_ERROR (the valid range is -20000 to -20999).
Share and enjoy.
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.