为啥我的异常没有被提出?

Posted

技术标签:

【中文标题】为啥我的异常没有被提出?【英文标题】:Why is my exception not being raised?为什么我的异常没有被提出? 【发布时间】:2017-04-19 21:23:41 【问题描述】:

我是 PL/SQL 和 Oracle 的新手。我正在研究一个小代码块,它会在一列中生成前 5 个值。当表中的数据少于 5 行时,我试图抛出异常。

DELETE FROM CREDIT_CARD;
SELECT * FROM CREDIT_CARD;
Insert into CREDIT_CARD values (6011956844573649,'550',892);
Insert into CREDIT_CARD values (5250335443644204,'15000',661);


DECLARE
   CURSOR Final_Q2_Cursor is
      SELECT CREDITCARDNUMBER,CREDITCARDLIMIT,CUSTOMERID FROM CREDIT_CARD
         ORDER BY CREDITCARDLIMIT DESC;   -- start with highest paid employee

  aCardLimt NUMBER(8,2);
  aCardNum NUMBER(16);
  aCustId INT;
BEGIN
   OPEN  Final_Q2_Cursor ;
   FOR i IN 1..5 LOOP
      FETCH Final_Q2_Cursor INTO aCardNum, aCardLimt, aCustId;
      EXIT WHEN Final_Q2_Cursor%NOTFOUND; 
      INSERT INTO TOP_FIVE_CREDIT_LIMITS VALUES (aCardLimt, aCardNum, aCustId);
   END LOOP;
   CLOSE Final_Q2_Cursor;
EXCEPTION 
   WHEN no_data_found THEN 
      dbms_output.put_line('No such customer!'); 
   WHEN others THEN 
      dbms_output.put_line('Error!');    
END;
/

我只插入 2 行数据,但它仍在处理所有内容。为什么?

【问题讨论】:

您预计会引发哪个异常?循环在第二次迭代后结束(因为 EXIT),但这不是例外。循环结束后,光标关闭(仍然没有异常),block结束。 我试图引发others 错误。所以我需要将我的异常放在 for 循环中? 无论您将支票放在哪里,此代码都不会引发异常。如果要在少于 5 行的情况下引发异常,请在循环内添加一个变量并在每次迭代时递增它;然后,就在循环之外,您可以检查此变量的值,如果它小于 5,则引发异常 (raise_application_error)。但是,我认为这是一个练习,因为我会使用完全不同的方法来做你需要的事情 @Aleksej 是的,这是一个课堂练习。想出不同的方式来使用游标循环。 【参考方案1】:

这是一个询问光标属性以确定是否需要显式引发错误的示例

SQL> create table TOP_FIVE_CREDIT_LIMITS (CREDITCARDLIMIT int, CREDITCARDNUMBER int, CUSTOMERID int );

Table created.

SQL> create table CREDIT_CARD (
  2        CREDITCARDNUMBER int,CREDITCARDLIMIT int,CUSTOMERID int );

Table created.

SQL>
SQL> DELETE FROM CREDIT_CARD;

0 rows deleted.

SQL> SELECT * FROM CREDIT_CARD;

no rows selected

SQL> Insert into CREDIT_CARD values (6011956844573649,'550',892);

1 row created.

SQL> Insert into CREDIT_CARD values (5250335443644204,'15000',661);

1 row created.

SQL>

PL/SQL:

SQL> set serverout on
SQL> DECLARE
  2     CURSOR Final_Q2_Cursor is
  3        SELECT CREDITCARDNUMBER,CREDITCARDLIMIT,CUSTOMERID FROM CREDIT_CARD
  4           ORDER BY CREDITCARDLIMIT DESC;   -- start with highest paid employee
  5
  6    aCardLimt NUMBER(8,2);
  7    aCardNum NUMBER(16);
  8    aCustId INT;
  9  BEGIN
 10     OPEN  Final_Q2_Cursor ;
 11     FOR i IN 1..5 LOOP
 12        FETCH Final_Q2_Cursor INTO aCardNum, aCardLimt, aCustId;
 13
 14        if Final_Q2_Cursor%NOTFOUND and i < 5 then
 15             raise_application_error(-20000,'Ran out of rows before I got to 5');
 16        end if;
 17        EXIT WHEN Final_Q2_Cursor%NOTFOUND;
 18        INSERT INTO TOP_FIVE_CREDIT_LIMITS VALUES (aCardLimt, aCardNum, aCustId);
 19     END LOOP;
 20     CLOSE Final_Q2_Cursor;
 21  EXCEPTION
 22     WHEN no_data_found THEN
 23        dbms_output.put_line('No such customer!');
 24     WHEN others THEN
 25        dbms_output.put_line('Error!');
 26  END;
 27  /
Error!

PL/SQL procedure successfully completed.

但我强调,这不是一种聪明的做事方式(尽管如果它是你的任务......那么很好)。通常,我们会尽可能地使用 SQL 来保持代码的简洁和精简,并且我们从不喜欢在 when-others 中捕获所有错误而不将它们传播回调用环境。所以你也可以像下面这样重铸你的代码:

SQL> set serverout on
SQL> BEGIN
  2    insert into TOP_FIVE_CREDIT_LIMITS
  3    select *
  4    from
  5      ( SELECT CREDITCARDNUMBER,CREDITCARDLIMIT,CUSTOMERID FROM CREDIT_CARD
  6        ORDER BY CREDITCARDLIMIT DESC
  7      )
  8    where rownum <= 5;
  9
 10    if sql%rowcount = 0 then
 11         raise_application_error(-20000,'No rows were found at all');
 12    elsif sql%rowcount < 5 then
 13         raise_application_error(-20000,'Ran out of rows before I got to 5');
 14    end if;
 15
 16  END;
 17  /
BEGIN
*
ERROR at line 1:
ORA-20000: Ran out of rows before I got to 5
ORA-06512: at line 13

【讨论】:

以上是关于为啥我的异常没有被提出?的主要内容,如果未能解决你的问题,请参考以下文章

如果我参考基类捕获它,为啥我的异常会被切分到基类?

引发异常或引发异常()

为啥我的 JScript(Windows 脚本宿主)在未捕获的异常中以 0 退出?

StackPanel 中的网格:为啥 auto 和 * 行为异常?

为啥 Rust RwLock 在 fork 中表现异常?

为啥 set_exception_handler(func) 不捕获异常?