发生异常时从游标获取值

时间:2015-01-14 14:02:46

标签: oracle plsql cursor

我有一个for循环的游标,当col2为零时会失败:

declare
    cursor cur_data is
    select id, col1/col2 as mean 
    from my_table;
begin
    for rec_data in cur_data loop
        update my_table set col3=rec_data.mean where id=rec_data.id;
    end loop;
exception when others then
    insert into my_log (id, error_text) values (rec_data.id, SQLERRM);
end; 

我希望在发生此错误时获取id列的值。类似于异常部分中的insert语句,当然因光标关闭而失败。有可能吗?

3 个答案:

答案 0 :(得分:2)

不需要声明变量:

declare
    cursor cur_data is
    select id, col1/col2 as mean 
    from my_table;
    v_id my_table.id%type;
begin
    for rec_data in cur_data loop
        v_id := rec_data.id;
        update my_table set col3=rec_data.mean where id=rec_data.id;
    end loop;
exception when others then
    insert into my_log (id, error_text) values (v_id, SQLERRM);
end; 

如果在循环中处理错误,可以使用rec_data.id:

declare
    cursor cur_data is
    select id, col1/col2 as mean 
    from my_table;
begin
    for rec_data in cur_data loop
        begin
            update my_table set col3=rec_data.mean where id=rec_data.id;
        exception when others then
            insert into my_log (id, error_text) values (rec_data.id, SQLERRM);
        end;
    end loop;
end; 

在此代码中,处理将从光标的下一行继续,而不是中止。要使其停止循环,您可以添加exit语句:

declare
    cursor cur_data is
    select id, col1/col2 as mean 
    from my_table;
begin
    for rec_data in cur_data loop
        begin
            update my_table set col3=rec_data.mean where id=rec_data.id;
        exception when others then
            insert into my_log (id, error_text) values (rec_data.id, SQLERRM);
            exit;
        end;
    end loop;
end; 

答案 1 :(得分:0)

首先,有几种方法不必面对这个问题。

1)在col2上定义一个不允许零值的检查约束

2)如果零值因其他原因有效,只需过滤掉定义光标的查询中的零值:

cursor cur_data is
select id, col1/col2 as mean 
from my_table
where col2 != 0; -- or "nvl( col2, 0 ) != 0" if nulls allowed

与此同时,如果您想查看发生这种情况的行,只需查询 零的位置。

答案 2 :(得分:0)

当您执行SELECT ID, 1/0 FROM my_table时,无论您的桌子有多大,您都不会选择任何内容,因此您没有任何ID。 divisor is equal to zero期间已提出异常SELECT,而UPDATE则异常。

这个工作正常。

CREATE TABLE my_table(
  ID NUMBER,
  nominator NUMBER,
  denominator NUMBER,
  ratio NUMBER CONSTRAINT ratio_check CHECK(ratio <= 1) );


CREATE TABLE my_log (
  ID NUMBER,
  error_text VARCHAR2(2000));


INSERT INTO my_table VALUES (400, 3, 5, NULL);
INSERT INTO my_table VALUES (500, 2, 5, NULL);
INSERT INTO my_table VALUES (300, 4, 5, NULL);
INSERT INTO my_table VALUES (100, 6, 5, NULL);
INSERT INTO my_table VALUES (200, 1, 5, NULL);
INSERT INTO my_table VALUES (600, 1, 0, NULL);
COMMIT;


DECLARE

  CURSOR cur_data IS
  SELECT ID, nominator/denominator AS ratio
  FROM my_table;

BEGIN

  FOR aRow IN cur_data LOOP
  BEGIN
    UPDATE my_table SET ratio = aRow.ratio WHERE ID = aRow.ID;
  EXCEPTION
    WHEN OTHERS THEN
        INSERT INTO my_log VALUES (aRow.ID, DBMS_UTILITY.FORMAT_ERROR_STACK||DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
  END;
  END LOOP;


EXCEPTION
    WHEN OTHERS THEN
        INSERT INTO my_log VALUES (-1, DBMS_UTILITY.FORMAT_ERROR_STACK||DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
END;

SELECT * FROM my_log;

ID     ERROR_TEXT
--------------------------------------------------------------------------------
100    ORA-02290: check constraint (MY_USER.RATIO_CHECK) violated                       
       ORA-06512: at line 11                                                           

-1     ORA-01476: divisor is equal to zero                                             
       ORA-06512: at line 9                                                            

2 rows selected.

如果你更喜欢FORALL,你可以使用这个:

DECLARE

   TYPE ratioRecType IS RECORD (ID NUMBER, ratio NUMBER);
   TYPE ratioTable IS TABLE OF ratioRecType;
   r ratioTable;

    DML_ERRORS EXCEPTION;
   PRAGMA EXCEPTION_INIT(DML_ERRORS, -24381);
    recId NUMBER;
    errMsg VARCHAR2(1000);

BEGIN

  SELECT ID, nominator/denominator
  BULK COLLECT INTO r
  FROM my_table
  WHERE denominator <> 0;

  FORALL i IN r.FIRST..r.LAST SAVE EXCEPTIONS
    UPDATE my_table SET ratio = r(i).ratio WHERE ID = r(i).ID;

EXCEPTION
  WHEN DML_ERRORS THEN
    FOR f IN 1..SQL%BULK_EXCEPTIONS.COUNT LOOP
       recId := r(SQL%BULK_EXCEPTIONS(f).ERROR_INDEX).ID;
       errMsg := SQLERRM(-SQL%BULK_EXCEPTIONS(f).ERROR_CODE);
       INSERT INTO my_log VALUES (recId, errMsg||CHR(13)||DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
     END LOOP;
  WHEN OTHERS THEN
     INSERT INTO my_log VALUES (-1, DBMS_UTILITY.FORMAT_ERROR_STACK||DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);

END;

SAVE EXCEPTIONS提供的详细错误消息不太明确,即

SELECT * FROM my_log;

ID     ERROR_TEXT
--------------------------------------------------------------------------------
100    ORA-02290: check constraint (.) violated                       
       ORA-06512: at line 19                                                         

为了获得所有记录,您也可以这样做:

DECLARE
  CURSOR cur_data IS
  SELECT ID, nominator, denominator
  FROM my_table;

BEGIN

  FOR aRow IN cur_data LOOP
  BEGIN
    UPDATE my_table SET ratio = aRow.nominator / aRow.denominator WHERE ID = aRow.ID;

...