【问题标题】:How to determine row/value throwing error in PL/SQL statement?如何确定 PL/SQL 语句中的行/值抛出错误?
【发布时间】:2010-10-31 00:04:39
【问题描述】:

(Oracle PL/SQL)

如果我有一个抛出错误的简单 SQL 语句,即:

DECLARE
    v_sql_errm varchar2(2048);
BEGIN
    UPDATE my_table SET my_column = do_something(my_column)
        WHERE my_column IS NOT NULL;
EXCEPTION
    when others then
        -- How can I obtain the row/value causing the error (unknown)?
        v_sql_errm := SQLERRM;
        insert into log_error (msg) values ('Error updating value (unknown): '||
             v_sql_errm);
END;

异常块中是否有任何方法可以确定查询遇到错误的行/值?我希望能够记录它,以便我可以进入并修改/更正导致错误的特定数据值。

【问题讨论】:

标签: oracle exception error-handling plsql


【解决方案1】:

如果您使用的是 10gR2 或更高版本,这可以使用 DML 错误日志记录来完成。

一个例子:

SQL> create table my_table (my_column)
  2  as
  3  select level from dual connect by level <= 9
  4  /

Tabel is aangemaakt.

SQL> create function do_something
  2  ( p_my_column in my_table.my_column%type
  3  ) return my_table.my_column%type
  4  is
  5  begin
  6    return 10 + p_my_column;
  7  end;
  8  /

Functie is aangemaakt.

SQL> alter table my_table add check (my_column not in (12,14))
  2  /

Tabel is gewijzigd.

SQL> exec dbms_errlog.create_error_log('my_table')

PL/SQL-procedure is geslaagd.

这将创建一个名为 err$_my_table 的错误记录表。通过在更新语句中添加日志错误子句来填充此表:

SQL> begin
  2    update my_table
  3       set my_column = do_something(my_column)
  4     where my_column is not null
  5           log errors reject limit unlimited
  6    ;
  7  end;
  8  /

PL/SQL-procedure is geslaagd.

SQL> select * from err$_my_table
  2  /

                       ORA_ERR_NUMBER$
--------------------------------------
ORA_ERR_MESG$
--------------------------------------------------------------------
ORA_ERR_ROWID$
--------------------------------------------------------------------
OR
--
ORA_ERR_TAG$
--------------------------------------------------------------------
MY_COLUMN
--------------------------------------------------------------------
                                  2290
ORA-02290: check constraint (RWK.SYS_C00110133) violated
AAGY/aAAQAABevcAAB
U

12

                                  2290
ORA-02290: check constraint (RWK.SYS_C00110133) violated
AAGY/aAAQAABevcAAD
U

14


2 rijen zijn geselecteerd.

在 10gR2 之前,您可以使用 SAVE EXCEPTIONS 子句:http://rwijk.blogspot.com/2007/11/save-exceptions.html

【讨论】:

  • 我通读了这个,以及你的博客链接,除非我有误解,否则这仍然只会给我错误消息(即 SQLCODE 和 SQLERRM),而不是触发错误的数据值。不幸的是,这没有帮助。顺便说一句,我坚持使用 9g。
  • 如果您仔细查看 err$_my_table 的内容,您会注意到 my_column 表示 12 和 14,它们是未能更新的新值。我将发布另一个答案,专门针对您的 9i 版本。也因为看到你选择逐行的方法让我的眼睛很痛:-)
【解决方案2】:

使用 SAVE EXCEPTIONS 子句的解决方案:

SQL> create table my_table (my_column)
  2  as
  3  select level from dual connect by level <= 9
  4  /

Table created.

SQL> create function do_something
  2  ( p_my_column in my_table.my_column%type
  3  ) return my_table.my_column%type
  4  is
  5  begin
  6    return 10 + p_my_column;
  7  end;
  8  /

Function created.

SQL> alter table my_table add check (my_column not in (12,14))
  2  /

Table altered.

SQL> declare
  2    e_forall_error exception;
  3    pragma exception_init(e_forall_error,-24381)
  4    ;
  5    type t_my_columns is table of my_table.my_column%type;
  6    a_my_columns t_my_columns := t_my_columns()
  7    ;
  8  begin
  9    select my_column
 10           bulk collect into a_my_columns
 11      from my_table
 12    ;
 13    forall i in 1..a_my_columns.count save exceptions
 14      update my_table
 15         set my_column = do_something(a_my_columns(i))
 16       where my_column = a_my_columns(i)
 17    ;
 18  exception
 19  when e_forall_error then
 20    for i in 1..sql%bulk_exceptions.count
 21    loop
 22      dbms_output.put_line(a_my_columns(sql%bulk_exceptions(i).error_index));
 23    end loop;
 24  end;
 25  /
2
4

PL/SQL procedure successfully completed.

对于非常大的数据集,您可能不想炸毁 PGA 内存,因此请务必在这种情况下使用 LIMIT 子句。

【讨论】:

    【解决方案3】:

    尝试输出您的错误,看看它是否为您提供了您正在寻找的信息。例如:

    EXCEPTION
        WHEN OTHERS
        THEN
          DBMS_OUTPUT.PUT_LINE(SQLERRM);
    END;
    

    【讨论】:

    • 谢谢,但这只是对错误的描述,例如日期操作的“月份无效”或值超过列大小时的“值太大”。我需要知道是哪个值导致了该错误,以便对数据进行适当的更正。
    【解决方案4】:

    有关执行如何到达相关行的更多详细信息,您可以尝试显示这些函数返回的输出:

    • DBMS_UTILITY.format_error_stack:

      格式化当前错误堆栈。这可用于异常处理程序以查看完整的错误堆栈。

    • DBMS_UTILITY.format_error_backtrace:

      格式化从当前错误点到捕获错误的异常处理程序的回溯。如果当前没有处理错误,则返回 NULL 字符串。

    【讨论】:

      【解决方案5】:

      试试这个(未测试):

          DECLARE
            cursor c1 is
              select key_column, my_column
              from my_table
              WHERE my_column IS NOT NULL
              ORDER BY key_column;
      
            my_table_rec my_table%ROWTYPE;
      
          BEGIN
      
            FOR my_table_rec in c1
            LOOP
              UPDATE my_table SET my_column = do_something(my_column)
                  WHERE key_column = my_table_rec.key_column;
            END LOOP;
      
          EXCEPTION
              when others then
              insert into log_error (msg) values ('Error updating key_column: ' || my_table_rec.key_column || ', my_column: ' || my_table_rec.my_column);
          END;
      

      【讨论】:

      • -1 用于建议从单个更新语句移动到逐行(也称为逐行)处理。
      • 好的,抢!你必须牢记“你不能一边吃蛋糕一边吃”。我的解决方案有效。你的呢?
      • 是的,我曾想过单独遍历它们并以这种方式得到它......虽然它是一个巨大的数据集,但希望无需迭代就能直接得到答案(并在同时!)。这是一个很好的建议,谢谢!
      • @Christian:正如您在此页面上看到的,我的解决方案也可以:-)
      • 请注意,对于其他尝试使用此功能的人,您需要将“FOR UPDATE”添加到光标上的选择中。这不是我希望的解决方案,但它似乎是根据此处给出的答案实际获得价值的唯一方法。
      【解决方案6】:

      PL/SQL 定义了 2 个全局变量来引用错误:

      SQLERRM : SQL 错误消息

      SQLERRNO:SQL 错误编号

      这在您的 PL/SQL 的 EXCEPTION 块中是可读的。

      DECLARE 
          x number;
      BEGIN
          SELECT 5/0 INTO x FROM DUAL;
      EXCEPTION
          WHEN OTHERS THEN:
              dbms_output.put_line('Error Message: '||SQLERRM);
              dbms_output.put_line('Error Number: '||SQLERRNO);
      END;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多