忽略导致错误的行

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 来表示调用过程中的错误。但它可能需要调整以使其健壮。

【讨论】:

以上是关于忽略导致错误的行的主要内容,如果未能解决你的问题,请参考以下文章

使用正则表达式仅选择带有错误代码 (-) 的行并忽略其他行

如何忽略错误但不跳过 redshift 复制命令中的行

有没有办法告诉 Stata 执行整个 do-file 忽略产生异常(甚至语法错误)的行?

Android studio - Proguard 似乎忽略了规则,导致构建错误

Oracle数据导入导出imp/exp :未知的命令开头。。。忽略了剩余的行

Python BigQuery - 如果行是新的并忽略重复的行,如何附加到表中