I am getting a problem with using 'Next Sequence Values' inside a procedure.
I have my 'external' function to return the Next Value of Sequence.
Inside my procedure, i want to assign the values of that sequence into a variable
and use that variable inside my cursor (since i am using a union statement in my cursor).
But it is not working.
CREATE OR REPLACE Procedure HR.insert_TBL_APP is
--declare variables for insert
v_TA_SNO VARCHAR2(10);
v_TA_SEQNO VARCHAR2(6);
v_TA_DESC VARCHAR2(10);
--declare variable to store the sequence number
var_TaSeqno varchar2(6);
-- Validation
v_check VARCHAR2 (10 Byte);
err_code varchar2(50);
err_msg varchar2(100);
v_table_name varchar2(50):='TBL_APP';
error_found exception;
cursor c1 is
select distinct TA_SNO,
TA_SEQNO,
TA_DESC
from (
SELECT hdr.FIRST_NO TA_SNO,
var_TaSeqno TA_SEQNO, -- using variable to assign the sequence no
hdr.descrip TA_DESC
FROM
FORMS_HDR hdr
WHERE
hdr.seco_name = 'TST121'
union
SELECT hdr.FIRST_NO TA_SNO,
var_TaSeqno TA_SEQNO, -- using variable to assign the sequence no
hdr.descrip TA_DESC
FROM
FORMS_HDR hdr
WHERE
hdr.seco_name = 'TST122');
begin
if c1%isopen then
close c1;
end if;
v_check:=null;
FOR i IN c1 LOOP
--assign variables for insert
v_TA_SNO := i.TA_SNO;
v_TA_SEQNO := i.TA_SEQNO;
v_TA_DESC := i.TA_DESC;
begin
-- calling the Function taSeqNoFunc and assign the
--sequence No into the variable var_TaSeqno
var_TaSeqno := HR.taSeqNoFunc();
select TA_SNO
into v_check
from TBL_APP a
where TA_SNO = i.TA_SNO
and TA_SEQNO =i.TA_SEQNO;
exception
when no_data_found then
--insert into target table
INSERT INTO TBL_APP (TA_SNO,
TA_SEQNO,
TA_DESC
)
values (v_TA_SNO,
v_TA_SEQNO,
v_TA_DESC
);
when others then
raise error_found;
end ;
end loop;
exception when error_found then
rollback;
err_code := SQLCODE;
err_msg := SUBSTR(SQLERRM, 1, 200);
insert into TA_ERROR_LOG values (v_check,v_table_name,'An error
was encountered '||err_code||':'||err_msg,sysdate);
commit;
raise_application_error(err_code,err_msg);
end;
/
The error finally goes into raise_application_error near the end:
"ORA-21000: error number argument to raise_application_error of 1 is out of range"
Please help me. Thank you.
The raise_application_error() procedure is an Oracle PL/SQL built-in. It's provided so what we can associate a custom message to a user-defined exception. User-defined exceptions must have numbers in the range -20999 to -20000. Find out more.
You are getting this error because of this code in your inner EXCEPTION block:
when others then
raise error_found;
You are raising a user-defined exception but one you haven't associated to an error number with the EXCEPTION_INIT pragma. Find out more. So, Oracle defaults to SQLCODE = 1, SQLERRM = 'User-Defined Exception'.
Clearly 1 is outside the permitted range of raise_application_error(). Hence the error when you come to the outer EXCEPTION block.
The way to avoid this is to remove the ERROR_FOUND exception and rely on Oracle's default exception handling.
In the innermost block you want to re-raise any exception except NO_DATA_FOUND. The simplest way to do that is remove the WHEN OTHERS clause. Then, in the outer block you will have meaningful values for SQLCODE and SQLERRM which you can log. Then just use RAISE to propagate them up the stack....
exception
when others then
rollback;
err_code := SQLCODE;
err_msg := SUBSTR(SQLERRM, 1, 200);
insert into TA_ERROR_LOG values (v_check,v_table_name,'An error
was encountered '||err_code||':'||err_msg,sysdate);
commit;
raise;
end;
Not only will you not get an error from raise_application_error(), your log will contain a useful error number and message.
Incidentally, it is bad practice to use ROLLBACK and COMMIT like that in an EXCEPTION block. A better approach is to write a logging procedure which is covered by the AUTONOMOUS_TRANSACTION pragma. That way the logging won't interfere with the wider transaction. Find out more.
The error number you pass to the raise_application_error procedure must be a negative integer in the range -20000..-20999.
For user-defined exceptions SQLCODE returns always +1, so you're passing +1 as
the error number to the raise_application_error procedure and that's out of range.
I have posted the answer. It can be further improved.
The pragma exception_init is used to catch the error (in my case - Integrity Constraint).
I have removed the function to call 'next sequence value' since it can be called directly just before performing the insert.
#APC Thanks :)
CREATE OR REPLACE Procedure HR.insert_TBL_APP is
--declare variables for insert
v_TA_SNO VARCHAR2(10);
v_TA_SEQNO VARCHAR2(6);
v_TA_DESC VARCHAR2(10);
-- Validation
v_check VARCHAR2 (10 Byte);
err_code varchar2(50);
err_msg varchar2(200);
v_table_name varchar2(50):='TBL_APP';
error_found exception;
parent_not_found exception; -- use it to catch the Integrity Constraint
pragma exception_init(parent_not_found, -2291);
cursor c1 is
select distinct TA_SNO,
TA_SEQNO,
TA_DESC
from (
SELECT hdr.FIRST_NO TA_SNO,
-1 TA_SEQNO, -- assign a dummy value as sequence no
hdr.descrip TA_DESC
FROM
FORMS_HDR hdr
WHERE
hdr.seco_name = 'TST121'
union
SELECT hdr.FIRST_NO TA_SNO,
-2 TA_SEQNO, -- assign a dummy value as sequence no
hdr.descrip TA_DESC
FROM
FORMS_HDR hdr
WHERE
hdr.seco_name = 'TST122');
begin
if c1%isopen then
close c1;
end if;
v_check:=null;
FOR i IN c1 LOOP
--assign variables for insert
v_TA_SNO := i.TA_SNO;
v_TA_SEQNO := i.TA_SEQNO;
v_TA_DESC := i.TA_DESC;
begin
--var_TaSeqno := HR.taSeqNoFunc(); --the variable will not be used
select TA_SNO
into v_check
from TBL_APP a
where TA_SNO = i.TA_SNO
and TA_SEQNO =i.TA_SEQNO;
exception
when no_data_found then
Begin -- added line to use exception
--insert into target table
INSERT INTO TBL_APP (TA_SNO,
TA_SEQNO,
TA_DESC
)
values (v_TA_SNO,
TA_SEQNO.nextval, --insert the nextval directly
v_TA_DESC
);
--when the integrity constraint (parent key) error is thrown, insert into the TA_ERROR_LOG
exception
when parent_not_found then
rollback;
err_msg := SUBSTR(SQLERRM, 1, 200);
insert into TA_ERROR_LOG values (v_check,v_table_name, err_msg, sysdate);
commit;
end; -- end begin for using the exception
when others then
raise error_found;
end ;
end loop;
exception when error_found then
rollback;
err_code := SQLCODE;
err_msg := SUBSTR(SQLERRM, 1, 200);
insert into TA_ERROR_LOG values (v_check,v_table_name, err_msg, sysdate);
commit;
raise_application_error(err_code,err_msg);
end;
Related
if i have 3 insert statements in my plsql code.
lets assume them as insert_1, insert_2, insert_3.
my conditions be like : if insert_1 statement successfully executed then automatically insert_3 should be executed as well as if insert_3 executed successfully then insert_1 also should be executed automatically.
if both insert_1 and insert_3 fails then only insert_2 should be executed.
please write plsql code for this requirement.
You can create a package for this:
create a package and inside package body you try something like this.
create or replace PACKAGE BODY PKG_INSERT_QUERY AS
P_ERR_CODE number;
procedure finalPro
is
begin
P_ERR_CODE := 0;
insert into a values('a');
commit;
EXCEPTION WHEN OTHERS THEN ROLLBACK;
DBMS_OUTPUT.PUT_LINE('ERROR ' ||SQLERRM);
P_ERR_CODE := 1;
if(P_ERR_CODE < 1 )then
secondQ;
IF(P_ERR_CODE < 1) THEN
thirdQ;
END IF;
END IF;
end;
procedure secondQ
as
begin
insert into B values('B');
commit;
EXCEPTION WHEN OTHERS THEN ROLLBACK;
DBMS_OUTPUT.PUT_LINE('ERROR ' ||SQLERRM);
P_ERR_CODE := 2;
end;
procedure thirdQ
as
begin
insert into C values('C');
commit;
EXCEPTION WHEN OTHERS THEN ROLLBACK;
DBMS_OUTPUT.PUT_LINE('ERROR ' ||SQLERRM);
P_ERR_CODE := 3;
end;
end;
How to handle exception for each forall as insert data collection is different for both insert.
ERROR:
Error(37,8): PLS-00103: Encountered the symbol "EXCEPTION" when expecting one of the following: ( begin case declare end exit for goto if loop mod null pragma raise return select update when while with << continue close current delete fetch lock insert open rollback savepoint set sql execute commit forall merge pipe purge
Error(113,33): PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following: end not pragma final instantiable order overriding static member constructor map
create or replace PROCEDURE INSRT_MISSG_DATA_TO_TBL IS
l_errors number;
l_errno number;
l_msg varchar2(4000);
l_idx number;
TYPE t_payment IS TABLE OF PAYMENT%ROWTYPE;
TYPE t_payment_alternate_id IS TABLE OF PAYMENT_ALT_ID%ROWTYPE;
list_payment t_payment := t_payment();
list_payment_alternate_id t_payment_alternate_id := t_payment_alternate_id();
BEGIN
FORALL i IN 1 .. list_payment.COUNT SAVE EXCEPTIONS
INSERT INTO PAYMENT VALUES list_payment(i);
EXCEPTION
WHEN OTHERS THEN
l_errors := SQL%BULK_EXCEPTIONS.COUNT;
FOR i in 1 .. l_errors
LOOP
l_errno := SQL%BULK_EXCEPTIONS(i).ERROR_CODE;
l_msg := SQLERRM(-l_errno);
l_idx := SQL%BULK_EXCEPTIONS(i).ERROR_INDEX;
insert into ERROR_LOG
( ora_err_no, ora_err_msg, err_payment_id, err_event_id )
values
( l_errno, l_msg, list_payment(l_idx).payment_id, null );
END LOOP;
FORALL i IN 1 .. list_payment_alternate_id.COUNT SAVE EXCEPTIONS
INSERT INTO PAYMENT_ALT_ID VALUES list_payment_alternate_id(i);
EXCEPTION
WHEN OTHERS THEN
l_errors := SQL%BULK_EXCEPTIONS.COUNT;
FOR i in 1 .. l_errors
LOOP
l_errno := SQL%BULK_EXCEPTIONS(i).ERROR_CODE;
l_msg := SQLERRM(-l_errno);
l_idx := SQL%BULK_EXCEPTIONS(i).ERROR_INDEX;
insert into ERROR_LOG
( ora_err_no, ora_err_msg, err_payment_id, err_event_id )
values
( l_errno, l_msg, list_payment(l_idx).payment_id, null );
END LOOP;
END INSRT_MISSG_DATA_TO_PODS_TBL;
Here's a shortened version of your code; I guess you'll get the idea & hope it'll be OK. Basically, you need to enclose each FORALL into its own BEGIN-END block.
create or replace procedure ...
begin
begin
forall ...
insert into ...
exception
when others then ...
end;
begin
forall ...
insert into ...
exception
when others then ...
end;
end
I have below procedure where i am trying to track the exceptions into I_Log table.To test whether its working or not I have made a ORA-00933: SQL command not properly ended error in my query where I am trying to insert into I_OPTION table. When i run this procedure the dbms output line is printing the error below but its not getting inserted into I_Log table:
OTHERS exception in EXT_I_OPTION - ID:1000196-933----ORA-00933: SQL command not properly ended
Below is my procedure:
CREATE OR REPLACE PROCEDURE
"EXT_I_OPTION"(in_id IN NUMBER DEFAULT 0)
AS
err_code VARCHAR(100);
err_msg VARCHAR(100);
in_event_id NUMBER;
in_db_link VARCHAR2(50);
in_env_id NUMBER;
l_sql VARCHAR2(5000);
l_sql1 VARCHAR2(5000);
BEGIN
FOR I_row IN I_cur
LOOP
l_sql2 := INSERT INTO I_OPTION(ID)
select DISTINCT(SO.ID)
)
from Icard I;
END LOOP;
EXCEPTION WHEN OTHERS THEN
err_code := SQLCODE;
err_msg := SUBSTR(SQLERRM, 1, 200);
INSERT INTO I_log (I_ID)
VALUES (i_id);
RAISE;
COMMIT;
END ext_I_option;
It seems that you have a RAISE before COMMIT; this way, the error will be raised before doing COMMIT, so you don't find data in your log table.
According to suggestions, you should define a procedure to handle your log table:
CREATE OR REPLACE procedure i_LOG (...) AS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
Insert into I_LOG(...);
COMMIT;
END;
/
This procedure runs in a separate transaction, so it only does commit of log data, with no conflict with data you modify in your main procedure.
Then you should modify your error handling in this way, avoiding COMMIT statement, that can be really dangerous, saving partial, unconsistent data:
DBMS_OUTPUT.PUT_LINE('OTHERS exception in EXT_I_OPTION - ID:'||to_char(ID) || err_code || '----' || err_msg );
ins_I_LOG(...);
RAISE;
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;
I am having some troubles with this trigger. I created a procedure to check and see if salary is within a certain boundary. If it fails to fall within a certain range, raise the exception. The problem is even though the procedure compiles with no errors, the trigger can not find the procedure.
set serveroutput on;
create or replace procedure check_salary (
tmp_id in varchar2,
tmp_sal in number
)
IS
v_sal number(6,0) := tmp_sal;
v_min number(6,0);
v_max number(6,0);
ex_fail exception;
cursor cur_select is
select min_salary, job_id, max_salary
from jobs where job_id = tmp_id;
BEGIN
for rec_something in cur_select loop
v_min := rec_something.min_salary;
v_max := rec_something.max_salary;
if v_sal >= v_min and v_sal <= v_max then
raise ex_fail;
end if;
end loop;
exception
when ex_fail then
dbms_output.put_line('Invalid salary ' || v_sal || ' must be between ' || v_min || ' and ' || v_max ||'.');
END;
/
show errors;
create or replace trigger check_salary_trg
after insert or update on employees
for each row
declare
begin
IF UPDATING or INSERTING THEN
execute check_salary(:NEW.job_id, :NEW.salary);
end if;
end;
/
show errors;
The Error Message:
PROCEDURE check_salary compiled
No Errors.
TRIGGER check_salary_trg compiled
Warning: execution completed with warning
5/13 PLS-00103: Encountered the symbol "CHECK_SALARY" when expecting one of the following:
:= . ( # % ; immediate
The symbol ":=" was substituted for "CHECK_SALARY" to continue.
Change it to:
create or replace trigger check_salary_trg
after insert or update on employees
for each row
begin
IF UPDATING or INSERTING THEN
check_salary(:NEW.job_id, :NEW.salary);
end if;
end;
/
When you are executing a procedure within a PL/SQL block, you do not use the
EXECUTE syntax
More information about execute you can check the below link
http://docstore.mik.ua/orelly/oracle/prog2/ch23_01.htm
The stack overflow exception is due to the use of dbms_output.put_line inside check_salary procedure.
SQL*Plus command set serveroutput on reserves little size as default, you must specify the buffer size or remove the dbms_output.put_line from check_salary procedure.
In order to increase default buffer size use this:
set serveroutput on size 1000000