How do you rollback to the next iteration in a loop? - sql

To explain my question easier I will just paste my whole code:
drop table tst;
create table tst
(t1 number(2));
set serveroutput on
DECLARE
TYPE vltp IS TABLE OF NUMBER(3);
vl vltp := vltp(2,12,33,344,55,66,7,555,4);
NUMBER_TO_BIG EXCEPTION;
PRAGMA EXCEPTION_INIT(NUMBER_TO_BIG, -01438);
BEGIN
FOR i IN vl.FIRST .. vl.LAST LOOP
INSERT INTO tst VALUES (vl(i));
SAVEPOINT ONE;
END LOOP;
EXCEPTION
WHEN NUMBER_TO_BIG THEN
ROLLBACK TO SAVEPOINT ONE;
END;
/
select * from tst;
Basically, when I am inserting 344 into the table I get an exception (NUMBER_TO_BIG) and I want it to roll back to the loop but skip that number.
The expected output:
tst
-----
2
12
33
55
66
7
4
Actual output:
no rows selected
It is rolling back all the changes, not just that one number.
Any ideas?

You should handle the exception inside the loop itself. It will continue with the loop, once the exception is handled.
SQL> DECLARE
TYPE vltp IS TABLE OF NUMBER(3);
vl vltp := vltp(2,12,33,344,55,66,7,555,4);
NUMBER_TO_BIG EXCEPTION;
PRAGMA EXCEPTION_INIT(NUMBER_TO_BIG, -01438);
BEGIN
FOR i IN vl.FIRST .. vl.LAST LOOP
BEGIN
INSERT INTO tst VALUES (vl(i));
EXCEPTION
WHEN NUMBER_TO_BIG THEN
NULL;
END;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
/
PL/SQL procedure successfully completed.
SQL> SELECT * FROM tst;
T1
----------
2
12
33
55
66
7
4
7 rows selected.

you should try this...
drop table tst;
--create table
create table tst
(t1 number(2));
--start of code
DECLARE
TYPE vltp IS TABLE OF NUMBER(3);
vl vltp := vltp(2, 12, 33, 344, 55, 66, 7, 555, 4);
NUMBER_TO_BIG EXCEPTION;
PRAGMA EXCEPTION_INIT(NUMBER_TO_BIG, -01438);
BEGIN
FOR i IN vl.FIRST .. vl.LAST LOOP
begin
INSERT INTO tst VALUES (vl(i));
exception
when NUMBER_TO_BIG then
--log exeption into log table here
dbms_output.put_line(sqlerrm);
end;
END LOOP;
commit;
exception
when others then
--log exeption into log table here
dbms_output.put_line(sqlerrm);
END;

Hello i have illustrtaed a small snippet to replicate your scenario. Let me know if this helps.
--Check for existing table with same name and dropping if already exists
DROP TABLE tst;
--Create Table
CREATE TABLE tst
(t1 NUMBER(2)
);
--Anonymous block to perform the task
SET serveroutput ON
DECLARE
TYPE vltp
IS
TABLE OF NUMBER
(
3
)
;
vl vltp := vltp(2,12,33,344,55,66,7,555,4);
NUMBER_TO_BIG EXCEPTION;
PRAGMA EXCEPTION_INIT(NUMBER_TO_BIG, -01438);
BEGIN
FOR i IN vl.FIRST .. vl.LAST
LOOP
BEGIN
INSERT INTO tst VALUES
(vl(i)
);
EXCEPTION
WHEN NUMBER_TO_BIG THEN
NULL;
dbms_output.put_line('skipping the value');
END;
END LOOP;
COMMIT;
END;

Related

I am trying to raise an error if 1 value is 1 or second value is 3, but I am getting some error? Any thoughts?

how to create a trigger just to prevent either to insert another if either 1 value is 1 or second one reached 3? if first is 1, 3 can still be inserted to second one, and if second one is 3, 1 still can be inserted into the first?
CREATE OR REPLACE TRIGGER tr_check_sum
BEFORE INSERT OF ON visitor
FOR EACH ROW
DECLARE
Maxnon NUMBER;
Maxpri NUMBER;
Maxlim NUMBER;
vl_out_of_range EXCEPTION;
BEGIN
SELECT permvisid, primaryvisqty, othervisqty, vislimit INTO Maxnon, Maxpri, Maxlim
FROM visqty
WHERE permvisid = :NEW.visresid;
IF (Maxpri = 1 OR Maxlim = 3) THEN
RAISE vl_out_of_range;
END IF;
EXCEPTION
WHEN vl_out_of_range THEN
Raise_application_error (
-20300,
'Visitor '|| 'limit' ||' out of range for '
);
WHEN NO_DATA_FOUND THEN
Raise_application_error(-20322, 'Invalid Job Classification');
END;
Something like:
CREATE OR REPLACE TRIGGER tr_check_sum
BEFORE INSERT ON visitor
FOR EACH ROW
DECLARE
Maxnon NUMBER;
Maxpri NUMBER;
Maxlim NUMBER;
vl_out_of_range EXCEPTION;
BEGIN
SELECT primaryvisqty,
othervisqty,
vislimit
INTO Maxnon,
Maxpri,
Maxlim
FROM visqty
WHERE permvisid = :NEW.visresid;
IF (Maxpri = 1 OR Maxlim = 3) THEN
RAISE vl_out_of_range;
END IF;
EXCEPTION
WHEN vl_out_of_range THEN
Raise_application_error(
-20300,
'Visitor limit out of range for '
);
WHEN NO_DATA_FOUND THEN
Raise_application_error(-20322, 'Invalid Job Classification');
END;
/
db<>fiddle here

how to do exception handling separately for more than one FORALL statement

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

sql insert procedure: insert values into table and control if value is existing in another table

I have little problem. I am trying to insert value into table. This is working. But I would like to control if value id_trainer is existing in another table. I want this -> execute insertClub(1, 5, 'someName'); -> and if id_trainer 5 not exists in table Trainer, procedure gives me message about this. (I tried to translate it into eng. lng., so you can find some gramm. mistakes)
create or replace procedure insertClub
(id_club in number, id_trainer in number, clubName in varchar2)
is
begin
declare counter number;
select count(*) into counter from trianer tr where tr.id_trainer = id_trainer;
if counter = 0 then
DBMS_OUTPUT.PUT_LINE('Trainer with this ID not exists');
end if;
insert into club values(id_club, id_trainer, clubName);
exception
when dup_val_on_index then
DBMS_OUTPUT.PUT_LINE('Dup ID');
end;
/
There is some error in the procedure. Please run below code to create procedure:
create or replace procedure insertClub
(id_club in number, id_trainer in number, clubName in varchar2)
is
counter number;
begin
select count(*) into counter from trianer tr where tr.id_trainer = id_trainer;
if counter = 0 then
DBMS_OUTPUT.PUT_LINE('Trainer with this ID not exists');
end if;
insert into club values(id_club, id_trainer, clubName);
exception
when dup_val_on_index then
DBMS_OUTPUT.PUT_LINE('Dup ID');
end;
/

Trigger error with column insert

I am creating a trigger for insert.If one of the value is the same as that in the old table. then I print a message. Here is my code.
Create or replace trigger TR_insert_act
After INSERT On ACTIVITIES
For each row
DECLARE
l_act varchar(30);
Begin
select Activity into l_act
From ACTIVITIES;
if(:new.Activity in l_act) then
DBMS_OUTPUT.PUT_LINE ('There is duplicate.');
end if;
end;
It is not compiled with the error on select of l_act, what to do please?
You need a BEFORE trigger, and a WHERE condition.
The DBMS_OUTPUT will not display the error though.
Create or replace trigger TR_insert_act
before INSERT On ACTIVITIES
For each row
l_act number;
Begin
select count(1) into l_act
From ACTIVITIES
WHERE ACTIVITY = :new.Activity
if(l_act > 0 ) then
RAISE_APPLICATION_ERROR (
num => -20000,
msg => 'There is Duplicate');
end if;
end;

creating a procedure for nested tables

The following codes successfully creates a procedure using sql*plus
CREATE OR REPLACE PROCEDURE input_order (pat_id in char, vis_vdate in date, vis_act in number,
vac_vacc in char)
AS
BEGIN
DBMS_OUTPUT.PUT_LINE ('Insert attempted');
insert into vaccinations(pid,vdate,action,vaccinated) values(pat_id,vis_vdate,vis_act,vac_vacc);
DBMS_OUTPUT.PUT_LINE ('Insert succeeded');
EXCEPTION
WHEN others THEN DBMS_OUTPUT.PUT_LINE ('error');
DBMS_OUTPUT.PUT_LINE ('Insert rejected');
END;
/
However my intension is to create a similar procedure which includes populating a nested table as an attribute of a table
for example: supposing 'vis_act' is a nested table
with type vis_set_t
and attributes visname and visurname
i tried it this way but kept getting errors
CREATE OR REPLACE PROCEDURE input_order (pat_id in char, vis_vdate in date, vis_act in
vis_set_t, vac_vacc in char)
AS
BEGIN
DBMS_OUTPUT.PUT_LINE ('Insert attempted');
insert into vaccinations(pid,vdate,vis_set_t(visname,visurname),vaccinated) values
(pat_id,vis_vdate,vis_act,vac_vacc);
DBMS_OUTPUT.PUT_LINE ('Insert succeeded');
EXCEPTION
WHEN others THEN DBMS_OUTPUT.PUT_LINE ('error');
DBMS_OUTPUT.PUT_LINE ('Insert rejected');
END;
/
General insert example.
As I mentioned in comments I'm not sure what are you trying to accomplish in your procedure. This is general insert example. Feel free to ask more questions...:
DECLARE
TYPE EmpTabTyp IS TABLE OF scott.emp%ROWTYPE;
emp_tab EmpTabTyp:= EmpTabTyp();
--
PROCEDURE insert_emp(p_col_typ IN EmpTabTyp)
IS
BEGIN
SELECT * BULK COLLECT INTO emp_tab FROM scott.emp;
--
FOR i IN p_col_typ.first .. p_col_typ.last
LOOP
-- Insert your values into your table --
INSERT Into emp_test (empno, ename) VALUES (p_col_typ(i).empno, p_col_typ(i).ename);
-- Optionally display values inserted - do not do this if table has many rows --
--DBMS_OUTPUT.put_line('INSERTED :'|| p_col_typ(i).empno||chr(9)||p_col_typ(i).ename);
END LOOP;
DBMS_OUTPUT.put_line('Total rows inserted :'||p_col_typ.Count);
END insert_emp;
BEGIN
insert_emp(emp_tab);
END;
/
In addition to my comment above:
DECLARE
TYPE EmpTabTyp IS TABLE OF scott.emp%ROWTYPE;
emp_tab EmpTabTyp:= EmpTabTyp();
PROCEDURE display_emp (p_col_typ IN EmpTabTyp)
IS
BEGIN
SELECT * BULK COLLECT INTO emp_tab FROM scott.emp;
--
FOR i IN p_col_typ.first .. p_col_typ.last LOOP
DBMS_OUTPUT.put_line(p_col_typ(i).empno||chr(9)||p_col_typ(i).ename);
END LOOP;
END display_emp;
BEGIN
display_emp(emp_tab);
END;
/
DECLARE
l_names DBMS_UTILITY.maxname_array; -- or DBMS_UTILITY.name_array
PROCEDURE show_contents (names_in IN DBMS_UTILITY.maxname_array)
IS
BEGIN
FOR indx IN names_in.FIRST .. names_in.LAST
LOOP
DBMS_OUTPUT.put_line (names_in (indx));
END LOOP;
END;
BEGIN
l_names (1) := 'Picasso';
l_names (2) := 'Keefe';
l_names (3) := 'Dali';
show_contents (l_names);
END;
/
-- returning collection type example:
DECLARE
TYPE t_emptbl IS TABLE OF scott.emp%rowtype;
v_emptbl t_emptbl;
ret_val t_emptbl;
Function getEmployeeList Return t_emptbl
IS
BEGIN
SELECT * bulk collect INTO v_emptbl FROM scott.emp;
-- Print nested table of records:
FOR i IN 1 .. v_emptbl.COUNT LOOP
DBMS_OUTPUT.PUT_LINE (v_emptbl(i).empno);
END LOOP;
RETURN v_emptbl;
END;
BEGIN
ret_val:= getEmployeeList;
END;
/