Consider a program to insert the elements in a PL/SQL collection into a database table. Its possible that some elements might fail and that the designer would regard this as a non-fatal error and want to continue to insert subsequent elements. The explicit row by row implementation would handle the exception, and probably record it for subsequent review thus
declare /* relies on... create table t ( text varchar2(3) ) */ type words_t is table of varchar2(10); words words_t := words_t ( 'dog', 'fish', 'cat', 'ball', 'bat', 'spoke', 'pad' ) /* 'ball' and 'spoke' will raise ORA-01401 */; n integer := 0; type error_indexes_t is table of integer index by binary_integer; error_indexes error_indexes_t; type error_codes_t is table of varchar2(255) index by binary_integer; error_codes error_codes_t; begin for j in words.first..words.last loop begin insert into t ( text ) values ( words(j) ); exception when others then n := n+1; error_indexes(n) := j; error_codes(n) := SQLERRM; end; end loop;
for j in 1..n
loop
Dbms_Output.Put_Line ( error_indexes(j) || ': ' || error_codes(j) );
end loop;
end;
Pre-Oracle9i there was no way to continue after a row-wise exception in the bulk binding approach
forall j in words.first..words.last insert into t ( text ) values ( words(j) );
and the effect of the ORA-01401 on [what would be] just some of the rows meant that no rows are inserted.
Oracle9i introduces the save exceptions syntax and the corresponding ORA-24381: error(s) in array DML exception. This allows the implied loop to continue after row-wise failure
forall j in words.first..words.last save exceptions /* new at 9i */ insert into t ( text ) values ( words(j) );
resulting in the successful insert of 'dog', 'cat', 'bat', 'pad'.
To complement this construct, the sql%bulk_exceptions collection allows reporting of the erroring rows in the exception handler for ORA-24381 thus
declare
type words_t is table of varchar2(10);
words words_t :=
words_t ( 'dog', 'fish', 'cat', 'ball', 'bat', 'spoke', 'pad' );
bulk_errors exception;
pragma exception_init ( bulk_errors, -24381 );
begin
forall j in words.first..words.last
save exceptions
insert into t ( text ) values ( words(j) );
exception when bulk_errors then
for j in 1..sql%bulk_exceptions.count
loop
Dbms_Output.Put_Line (
sql%bulk_exceptions(j).error_index || ', ' ||
Sqlerrm(-sql%bulk_exceptions(j).error_code) );
end loop;
end;
which produces
2: ORA-01401: inserted value too large for column
4: ORA-01401: inserted value too large for column
6: ORA-01401: inserted value too large for column
The construct is also supported in native dynamic SQL thus
forall j in words.first..words.last
save exceptions
execute immediate 'insert into t ( text ) values ( :the_word )'
using words(j);