忽略导致错误的行
Posted
技术标签:
【中文标题】忽略导致错误的行【英文标题】:Ignore lines that causes errors 【发布时间】:2020-07-20 14:29:27 【问题描述】:我有一个大型 Oracle 脚本,在 BEGIN
- END;
内调用了数千个包
有没有办法并继续执行下一行? vb 中的某种“On Error Resume Next”。
【问题讨论】:
这在很大程度上取决于您用于运行脚本的工具。你用的是什么工具?例如,旧的 Apache Ant 曾经在<sql>
任务中有一个属性 onerror="continue"
。
@TheImpaler 我不知道。我在标签中添加了工具
【参考方案1】:
如果您只有一个 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>
【讨论】:
由于 OP 想要一种方法来“忽略导致错误的行并继续执行下一行”,您需要将此应用于 “带有数千个包调用的大型 Oracle 脚本”. @WilliamRobertson 这是真的。不久前我尝试了这个想法,它忽略了错误之后的行。【参考方案2】:“忽略错误”的整个概念是一个错误,如果发生任何错误都是一个谎言。这并不是说您不能捕获错误并继续处理,只是您必须处理错误。例如,假设用例:“数据已从多个 .csv 文件加载到暂存表中。现在根据 .... 加载到表 A 和表 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\end。 不,只有在需要区分的地方。所以第二个例子可以写成第一个;只有 1 个内部开始/结束块(只是没有那么多细节),并且只有在异常不会导致终止的情况下 - 大多数应该。【参考方案3】:我认为没有任何神奇的单线可以解决这个问题。
与其他人一样,使用编辑器将每个调用自动包装在 BEGIN-EXCEPTION-END 块中可能会更快/更容易。
但是,如果觉得有点冒险,还是试试这个策略:
假设你有这个:
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;
这里的想法是添加一个“运行器”过程来动态执行每个过程并记录运行、跳过、错误。
我们维护当前进程号 (l_proc_no) 和执行步骤总数 (l_progress) 的全局计数。
当发生错误时,我们记录它,将其提升并让它落入外部块 EXCEPTION 处理程序中,它将通过(邪恶的)GOTO 重新启动。
放置 GOTO 时,总执行次数不变,但进程号重置为 0。
现在当 run_proc 被调用时,它会看到 l_progress 大于 l_proc_no,并跳过它。
为什么这比在每个调用周围简单地包装一个 BEGIN EXCEPTION END 更好?
可能不是,但是您对每一行代码进行了较小的更改,并且您可以更整齐地标准化每个调用的日志记录。
危险在于潜在的无限循环,这就是为什么我指定 e_proc_err 来表示调用过程中的错误。但它可能需要调整以使其健壮。
【讨论】:
以上是关于忽略导致错误的行的主要内容,如果未能解决你的问题,请参考以下文章
有没有办法告诉 Stata 执行整个 do-file 忽略产生异常(甚至语法错误)的行?
Android studio - Proguard 似乎忽略了规则,导致构建错误