忽略导致错误的行
Ignore lines that causes errors
我有一个大型 Oracle 脚本,在 BEGIN
- END;
中包含数千个包调用
有没有办法忽略导致错误的行并继续执行下一行? vb 中的某种“On Error Resume Next”。
如果您只有一个 BEGIN END 部分,那么您可以使用 EXCEPTION WHEN OTHERS THEN NULL。
SQL> declare
v_var pls_integer;
begin
select 1 into v_var from dual;
-- now error
select 'A' into v_var from dual;
exception when others then null;
end;
SQL> /
PL/SQL procedure successfully completed.
SQL> declare
v_var pls_integer;
begin
select 1 into v_var from dual;
-- now error
select 'A' into v_var from dual;
--exception when others then null;
end;
/
declare
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at line 6
SQL>
“忽略错误”的整个概念是一个错误,如果出现任何错误,就是一个谎言。这并不是说您不能捕获错误并继续处理,只是您必须处理错误。例如,假设用例:“数据已从多个 .csv 文件加载到阶段 table。现在根据...加载到 tables A 和 Table B .".
create procedure
Load_Tables_A_B_from_Stage(process_message out varchar2)
is
Begin
For rec in (select * from stage)
loop
begin
insert into table_a (col1, col2)
values (rec.col_a1, col_a2);
insert into table_b (col1, col2)
values (rec.col_b1, col_b2);
exception
when others then null;
end;
end loop;
process_message := 'Load Tables A,B Complete';
end ;
现在假设用户创建了一个 .csv 文件,在没有值或值未知的数字列中输入了“n/a”。这种太常见的情况的结果是所有这些行都没有加载,但是 你无法知道 直到用户抱怨他们的数据没有加载,即使你告诉他们它是. 而且您无法确定问题所在。
更好的方法是“捕获并报告”。
create procedure
Load_Tables_A_B_from_Stage(process_message out varchar2)
is
load_error_occurred boolean := False;
Begin
For rec in (select * from stage)
loop
begin
insert into table_a (col1, col2)
values (rec.col_a1, rec.col_a2);
exception
when others then
log_load_error('Load_Tables_A_B_from_Stage', stage_id, sqlerrm);
load_error_occurred := True;
end;
begin
insert into table_b (col1, col2)
values (rec.col_b1, rec.col_b2);
exception
when others then
log_load_error('Load_Tables_A_B_from_Stage', stage_id, sqlerrm);
load_error_occurred := True;
end;
end loop;
if load_error_occurred then
process_message := 'Load Tables A,B Complete: Error(s) Detected';
else
process_message := 'Load Tables A,B Complete: Successful No Error(s)';
end if;
end Load_Tables_A_B_from_Stage ;
现在您已经将实际情况告知用户,并且通过与您联系的方式可以很容易地确定问题所在。
此处的用户是最一般意义上的用户。这可能意味着调用程序而不是个人。重点是您不必因错误而终止您的进程,但不要忽略它们。
我不认为有任何神奇的单线可以解决这个问题。
与其他人一样,使用编辑器自动包装 BEGIN-EXCEPTION-END 块中的每个调用可能是 quicker/easier。
但是,如果觉得有点冒险,或者试试这个攻略:
假设您有这个:
BEGIN
proc1;
proc2;
proc3;
.
.
.
proc1000;
END;
您可以试试这个(未经测试、未编译但可能会让您知道要尝试什么):
DECLARE
l_progress NUMBER := 0;
l_proc_no NUMBER := 0;
e_proc_err EXCEPTION;
-- A 'runner' procedure than manegrs the counters and runs/skips dpending on these vals
PROCEDURE run_proc ( pname IN VARCHAR2 ) IS
BEGIN
l_proc_no := l_proc_no + 1;
IF l_proc_no >= l_progress
THEN
-- log 'Running pname'
EXECUTE IMMEDIATE 'BEGIN ' || pname || '; END;' ;
l_progress := l_progress + 1;
ELSE
-- log 'Skipping pname'
END IF;
EXCEPTION
WHEN OTHERS THEN
-- log 'Error in pname'
l_progress := l_progress + 1;
RAISE e_proc_err;
END;
BEGIN
l_progress := 0;
<<start>>
l_proc_no := 0;
run_proc ( 'proc1' );
run_proc ( 'proc2' );
run_proc ( 'proc3' );
.
.
run_proc ( 'proc1000' );
EXCEPTION
WHEN e_proc_err THEN
GOTO start;
WHEN OTHERS THEN
RAISE;
END;
这里的想法是添加一个 'runner' 过程来动态执行每个过程并记录 运行、跳过、错误。
我们维护当前进程号 (l_proc_no) 和执行步骤总数 (l_progress) 的全局计数。
当发生错误时,我们记录它,提出它并让它落入外部块 EXCEPTION 处理程序,它将通过(邪恶的)GOTO 重新启动。
放置GOTO使得总执行计数不变但进程号重置为0。
现在,当 run_proc 被调用时,它发现 l_progress 大于 l_proc_no,并跳过它。
为什么这比简单地在每个调用周围包装一个 BEGIN EXCEPTION END 更好?
可能不是,但您对每一行代码进行了较小的更改,并且更加整齐地标准化了每次调用的日志记录。
危险是潜在的无限循环,这就是为什么我指定 e_proc_err 来表示被调用过程中的错误。但它可能需要调整以使其健壮。
我有一个大型 Oracle 脚本,在 BEGIN
- END;
有没有办法忽略导致错误的行并继续执行下一行? vb 中的某种“On Error Resume Next”。
如果您只有一个 BEGIN END 部分,那么您可以使用 EXCEPTION WHEN OTHERS THEN NULL。
SQL> declare
v_var pls_integer;
begin
select 1 into v_var from dual;
-- now error
select 'A' into v_var from dual;
exception when others then null;
end;
SQL> /
PL/SQL procedure successfully completed.
SQL> declare
v_var pls_integer;
begin
select 1 into v_var from dual;
-- now error
select 'A' into v_var from dual;
--exception when others then null;
end;
/
declare
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at line 6
SQL>
“忽略错误”的整个概念是一个错误,如果出现任何错误,就是一个谎言。这并不是说您不能捕获错误并继续处理,只是您必须处理错误。例如,假设用例:“数据已从多个 .csv 文件加载到阶段 table。现在根据...加载到 tables A 和 Table B .".
create procedure
Load_Tables_A_B_from_Stage(process_message out varchar2)
is
Begin
For rec in (select * from stage)
loop
begin
insert into table_a (col1, col2)
values (rec.col_a1, col_a2);
insert into table_b (col1, col2)
values (rec.col_b1, col_b2);
exception
when others then null;
end;
end loop;
process_message := 'Load Tables A,B Complete';
end ;
现在假设用户创建了一个 .csv 文件,在没有值或值未知的数字列中输入了“n/a”。这种太常见的情况的结果是所有这些行都没有加载,但是 你无法知道 直到用户抱怨他们的数据没有加载,即使你告诉他们它是. 而且您无法确定问题所在。
更好的方法是“捕获并报告”。
create procedure
Load_Tables_A_B_from_Stage(process_message out varchar2)
is
load_error_occurred boolean := False;
Begin
For rec in (select * from stage)
loop
begin
insert into table_a (col1, col2)
values (rec.col_a1, rec.col_a2);
exception
when others then
log_load_error('Load_Tables_A_B_from_Stage', stage_id, sqlerrm);
load_error_occurred := True;
end;
begin
insert into table_b (col1, col2)
values (rec.col_b1, rec.col_b2);
exception
when others then
log_load_error('Load_Tables_A_B_from_Stage', stage_id, sqlerrm);
load_error_occurred := True;
end;
end loop;
if load_error_occurred then
process_message := 'Load Tables A,B Complete: Error(s) Detected';
else
process_message := 'Load Tables A,B Complete: Successful No Error(s)';
end if;
end Load_Tables_A_B_from_Stage ;
现在您已经将实际情况告知用户,并且通过与您联系的方式可以很容易地确定问题所在。
此处的用户是最一般意义上的用户。这可能意味着调用程序而不是个人。重点是您不必因错误而终止您的进程,但不要忽略它们。
我不认为有任何神奇的单线可以解决这个问题。
与其他人一样,使用编辑器自动包装 BEGIN-EXCEPTION-END 块中的每个调用可能是 quicker/easier。
但是,如果觉得有点冒险,或者试试这个攻略:
假设您有这个:
BEGIN
proc1;
proc2;
proc3;
.
.
.
proc1000;
END;
您可以试试这个(未经测试、未编译但可能会让您知道要尝试什么):
DECLARE
l_progress NUMBER := 0;
l_proc_no NUMBER := 0;
e_proc_err EXCEPTION;
-- A 'runner' procedure than manegrs the counters and runs/skips dpending on these vals
PROCEDURE run_proc ( pname IN VARCHAR2 ) IS
BEGIN
l_proc_no := l_proc_no + 1;
IF l_proc_no >= l_progress
THEN
-- log 'Running pname'
EXECUTE IMMEDIATE 'BEGIN ' || pname || '; END;' ;
l_progress := l_progress + 1;
ELSE
-- log 'Skipping pname'
END IF;
EXCEPTION
WHEN OTHERS THEN
-- log 'Error in pname'
l_progress := l_progress + 1;
RAISE e_proc_err;
END;
BEGIN
l_progress := 0;
<<start>>
l_proc_no := 0;
run_proc ( 'proc1' );
run_proc ( 'proc2' );
run_proc ( 'proc3' );
.
.
run_proc ( 'proc1000' );
EXCEPTION
WHEN e_proc_err THEN
GOTO start;
WHEN OTHERS THEN
RAISE;
END;
这里的想法是添加一个 'runner' 过程来动态执行每个过程并记录 运行、跳过、错误。
我们维护当前进程号 (l_proc_no) 和执行步骤总数 (l_progress) 的全局计数。
当发生错误时,我们记录它,提出它并让它落入外部块 EXCEPTION 处理程序,它将通过(邪恶的)GOTO 重新启动。
放置GOTO使得总执行计数不变但进程号重置为0。
现在,当 run_proc 被调用时,它发现 l_progress 大于 l_proc_no,并跳过它。
为什么这比简单地在每个调用周围包装一个 BEGIN EXCEPTION END 更好?
可能不是,但您对每一行代码进行了较小的更改,并且更加整齐地标准化了每次调用的日志记录。
危险是潜在的无限循环,这就是为什么我指定 e_proc_err 来表示被调用过程中的错误。但它可能需要调整以使其健壮。