简体   繁体   English

哪个命令引发了异常?

[英]Which command raised the exception?

Is there a variable like SQLERRM or SQLCODE that holds the statement which raised the error? 是否存在像SQLERRMSQLCODE这样的变量来保存引发错误的语句?

example: 例:

/* 
if some error raised from this code 
and I want to know which statement cause the failure..
I wish to use some oracle varaible to know it
*/
begin

select * from t1;
select * from t2;

exception when others
 dbms_output.put_line(sqlerrm || ' raised from this statement:' || <some_variable>;
end;

-- excepted result: no data found raised from this statement: select * from t2

Simple answer, no. 简单的答案,不。 You're losing some information by defining an exception handler. 通过定义异常处理程序,您将丢失一些信息。 With an unhandled exception you'd get an error message which includes the line number. 对于未处理的异常,您会收到一条包含行号的错误消息。 But obviously we need to handle errors, log them, etc. So not having a line number is pretty rubbish. 但是显然我们需要处理错误,记录错误等。因此,没有行号是很垃圾的。

Fortunately there are a couple of options. 幸运的是,有两种选择。 In older versions of Oracle we can use dbms_utility.format_error_backtrace() and dbms_utility.format_error_stack() to get some useful information, including the line numbers. 在旧版本的Oracle中,我们可以使用dbms_utility.format_error_backtrace()dbms_utility.format_error_stack()获得一些有用的信息,包括行号。 It's pretty unwieldy and (especially for the backtrace) verbose. 它非常笨拙,并且(特别是用于回溯)非常冗长。

In Oracle 12c we got a whole package devoted to PL/SQL call stack: UTL_CALL_STACK. 在Oracle 12c中,我们得到了一个专门用于PL / SQL调用堆栈的完整软件包:UTL_CALL_STACK。 It is a box of bits and requires more than one call to get things but we can retrieve a specific line number with unit_line() . 它是一盒位,需要多次调用才能得到,但是我们可以使用unit_line()检索特定的行号。 Tim Hall has written a typically fine introduction to the new feature. 蒂姆·霍尔(Tim Hall)对新功能进行了典型的精美介绍。 Find out more . 了解更多


The other thing to consider is how good program design can resolve this problem. 要考虑的另一件事是良好的程序设计如何解决此问题。 Specifically the Single Responsibility Principle . 特别是单一责任原则 This is a fundamental guideline of program design: a program unit should do one thing. 这是程序设计的基本准则:程序单元应该做一件事。 If we asking the question "which command through this error" it can be a sign that we're violating the SRP. 如果我们问“通过此错误执行哪个命令”的问题,则可能表明我们违反了SRP。

Let's resign your code so it follows this design principle: 让我们辞职代码,使其遵循以下设计原则:

declare
    type nt1 is table of t1%rowtype;
    type nt2 is table of t2%rowtype;
    l_t1 nt1;
    l_t2 nt2;
    x_t1_ndf exception;
    x_t2_ndf exception;

    function get_t1 return nt1 is
      return_value nt1;
    begin
      select * 
      bulk collect into return_value
      from t1;
      if  return_value.count() = 0 then 
        raise x_t1_ndf;
      end if; 
      return return_value;
  end get_t1;

  function get_t2 return nt2 is
    return_value nt2;
  begin
    select * 
    bulk collect into return_value
    from t2;
    if  return_value.count() = 0 then 
      raise x_t2_ndf;
    end if; 
    return return_value;
  end get_t2;
begin
  l_t1 := get_t1;
  l_t2 := get_t2;
exception 
  when x_t1_ndf then
      dbms_output.put_line('T1 has no data');
  when x_t2_ndf then
      dbms_output.put_line('T2 has no data');
end;

Obviously more typing than your original code but partly that's because this toy is complete working code, unlike the code you posted. 显然,键入的内容比原始代码多,但是部分原因是,此玩具是完整的工作代码,与您发布的代码不同。 Also in real life these modules would be discrete units, rather than private functions in an anonymous block, and so we could re-use them in multiple other programs. 同样在现实生活中,这些模块将是离散的单元,而不是匿名块中的私有功能,因此我们可以在其他多个程序中重复使用它们。

Also dbms_output.put_line() is not the proper way to handle exceptions, but I've left that because it's what your code does. dbms_output.put_line()也不是处理异常的正确方法,但是我已经离开了,因为这就是您的代码所做的事情。

There is no built-in that you can use for that. 没有内置功能可用于此目的。

One way could be by handling the exceptions of the single statements, with something like (pseudo-code): 一种方法是通过处理单个语句的异常,例如(伪代码):

declare
  err varchar2(100);
  myException exception;
begin
  ...

  begin
     select * from t1;
  exception
  when others then
    err := 'Error in select * from t1: ' || sqlerrm;
    raise myException
  end;

  begin
     select * from t2;
  exception
  when others then
    err := 'Error in select * from t2: ' || sqlerrm;
    raise myException
  end;

  ...

exception
 when myException then
  dbms_output.put_line(err);
 when others then
  dbms_output.put_line('Unhandled exception: ' || sqlerrm);
end;

For something more, this can be very useful. 对于更多的东西, 可能非常有用。

Using a single exception handler for multiple statements always mask the statement that caused an error. 对多个语句使用单个异常处理程序总是会掩盖导致错误的语句。

Instead, you can use a Local variable(Locator) to track statement execution, as follows: 相反,您可以使用Local变量(Locator)来跟踪语句执行,如下所示:

DECLARE
   err_stmt NUMBER:= 1;  -- Indicates 1st SELECT statement
BEGIN
   SELECT ... -- Statement 1
   err_stmt := 2;  -- Indicates 2nd SELECT statement
   SELECT ... -- Statement 2
EXCEPTION 
   WHEN OTHERS THEN
      dbms_output.put_line(sqlerrm || ' raised from this statement number:' || err_stmt;
END;

Cheers!! 干杯!!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM