如何处理 for 循环中的异常以使用存储过程插入行?甲骨文 PLSQL

Posted

技术标签:

【中文标题】如何处理 for 循环中的异常以使用存储过程插入行?甲骨文 PLSQL【英文标题】:How to handle exceptions in for loop for insert rows using a stored procedure? Oracle PLSQL 【发布时间】:2018-03-13 14:20:00 【问题描述】:

我正在编写一个复杂的 PLSQL 块(对我来说很复杂哈哈哈),以使用来自 FOR LOOP CURSOR 的信息插入行,并使用存储过程添加参数以插入。当前的问题是要插入大约 200 行,但是当一个简单的行无法插入所有插入的行时,oracle 会执行 ROLLBACK 命令(我认为是这样)。所以......我如何处理异常以成功插入所有回合,并且当任何行失败时在屏幕上显示它?谢谢

FOR i IN c_mig_saldos LOOP
        IF i.tipo_comprobante = 'P' THEN -- Nota de debito (positivo)
            v_cmp_p.prn_codigo := 'VIV';
            v_cmp_p.tcm_codigo := 'NRA';
            v_cmp_p.cmp_fecha_emision := TRUNC(SYSDATE);
            v_cmp_p.cmp_fecha_contable := TRUNC(SYSDATE);
            v_cmp_p.cmp_observacion := 'GENERACION AUTOMATICA DE SALDOS';
            v_cmp_p.cli_codigo := i.cli_codigo;

            v_tab_dco_p(1).cnc_codigo := 'VIA';
            v_tab_dco_p(1).dco_precio_unitario := i.total_final;
            v_tab_dco_p(1).dco_cantidad := 1;
            v_tab_dco_p(1).dco_importe := i.total_final;

            -- Insert a new row using stored procedure but when a itereted fail, no rows has inserted in table
            PKG_COMPROBANTES.PRC_INSERTAR_COMPROBANTE(v_cmp_p, v_tab_dco_p, v_tab_pgc_p, v_tab_apl_p, v_tab_mar_p); 
            COMMIT;
        END IF;
END LOOP;

【问题讨论】:

When should I nest PL/SQL BEGIN...END blocks?的可能重复 是的,你可以做你想做的事。但是,如果不知道插入的包调用失败的原因以及您想对此做什么,没有人可以正确地指导您。在不理解的情况下,任何人都可以做的就是引导您忽略失败的插入,这通常是一个错误的答案。 感谢迈克尔,存储过程传递参数信息以在某些表中插入新行,并返回(按表索引)有关插入的新单行的信息。我想在表中存储有关未插入行的信息。所以在块的末尾我想显示有关未插入的行的信息,后来保存在 txt 中(使用 toad 按钮)。有可能吗? 【参考方案1】:
-- Insert a new row using stored procedure but when a itereted fail, no rows has inserted in table
begin
    PKG_COMPROBANTES.PRC_INSERTAR_COMPROBANTE(v_cmp_p, v_tab_dco_p, v_tab_pgc_p, v_tab_apl_p, v_tab_mar_p);
    exception
        when others then --this could be made more specific but you didn't say what type of error you were getting
            -- Log to a table so you can export failed inserts later.
            -- Make sure log table cols are large enough to store everything that can possibly be inserted here...
            ErrorLogProc(the, cols, you, want, to, see, and, SQLERRM);
end;

在 ErrorLogProc() 中,我推荐几件事,这里是我在错误日志记录过程中所做的一些事情的 sn-p,您可能会觉得有帮助(它只是一些 sn-ps,并不打算完全工作,但你应该明白)...

oname varchar2(100);
pname varchar2(100);
lnumb varchar2(100);
callr varchar2(100);
g1B CHAR(2) := chr(13)||chr(10);

PRAGMA AUTONOMOUS_TRANSACTION;  --important!

begin

  owa_util.who_called_me(oname, pname, lnumb, callr);

  --TRIM AND FORMAT FOR ERRORLOG
  lv_errLogText := 'Package: '||pname||' // Version: '||version_in||' // Line Number: '||lnumb||' // Error: ';
  lv_string1    := mid(errStr_in,1,4000-Length(lv_errLogText));
  lv_errLogText := lv_errLogText||lv_string1;
  lv_errLogText := lv_errLogText||g1B||'Error Backtrace: '||replace(dbms_utility.format_error_backtrace,'ORA-', g1b||'ORA-');

  insertIntoYourErrorLogTable(lv_errLogText, and, whatever, else, you, need);

commit;

【讨论】:

【参考方案2】:

为了简单起见,由于没有足够的信息来了解问题的内容和原因,这将根据需要排除一些有关失败的文本信息。

SQL> set serveroutput on

那么这里是一个匿名的 PL/SQL 块:

BEGIN
    FOR i IN c_mig_saldos 
    LOOP
        -- not relevant

        BEGIN
            PKG_COMPROBANTES.PRC_INSERTAR_COMPROBANTE(v_cmp_p, v_tab_dco_p, v_tab_pgc_p, v_tab_apl_p, v_tab_mar_p); 
        EXCEPTION
            -- The goal of this is to ignore but output information about your failures
            WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('whatever you want about v_cmp_p, v_tab_dco_p, v_tab_pgc_p, v_tab_apl_p, v_tab_mar_p or SQLERRM/SQLCODE or the error stack - not enough info in the question');
        END;
    END LOOP;
END;
/

注意:我已从执行中删除了提交。 那么如果你喜欢你所看到的......

SQL> commit;

理想情况下,如果我知道更多关于预期会发生插入失败的原因以及我想对它们做什么,我一开始就不会插入它们。

【讨论】:

【参考方案3】:

同意需要更多信息的评论,但有几点需要考虑:

    这是否需要作为一个循环来完成 - 如果您可以将查询编写为一个 select 语句,那么您可以在不需要 PLSQL 的情况下进行简单的插入,这会更简单并且可能更快(基于集合的 SQL 与行逐行 PLSQL) 您说ROLLBACK 正在发生-您在IF 语句中有一个提交,因此任何进入您的IF 语句并成功通过您的插入过程的记录都将被提交,即它们不会被回滚;您应该考虑是否某些您认为正在回滚的记录实际上根本没有进入IF 语句

您能否提供示例数据或您收到的错误示例?

【讨论】:

以上是关于如何处理 for 循环中的异常以使用存储过程插入行?甲骨文 PLSQL的主要内容,如果未能解决你的问题,请参考以下文章

Informix 存储过程通用异常处理

如何处理循环中的每个输出行?

在表中找不到给定记录时如何处理游标中的异常

如何处理 jdbc 事务中的父键约束?

如何处理将 DBNull 传递给函数?

如何处理 Django 模型中的循环关系?