Oracle 发生异常时从游标获取值

Oracle 发生异常时从游标获取值,oracle,plsql,cursor,Oracle,Plsql,Cursor,我有一个游标用于循环,当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 othe

我有一个游标用于循环,当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列的值。类似于exception部分中的insert语句,这当然会失败,因为游标已关闭。是否可能?

否您需要声明一个变量:

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) 在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

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

当您从my_表中选择ID,1/0时无论您的表有多大,您始终不会选择任何内容,因此,您没有任何ID。异常
除数等于零
是在
选择
时引发的,而不是在
更新
时引发的

这个正在工作

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.
但是,
保存异常
提供了不太详细的错误消息,即

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;

...

当别人
本身就是一个bug时。先把它除掉。嗯,这对我有用。这些例子中有一个对我有用。我在屏幕上看到ORA错误,或者我的日志中的id列变为空。问题是异常发生在
SELECT
时,而不是在处理异常的
UPDATE
时。请看我的答案。这两种方法都没有给我id列的值,不过还是要谢谢你。我想这是不可能的,因为
从我的表中选择ID,1/0
不返回任何内容。也许我必须将公式从选择移动到更新…我知道这一点,但代码是从最终用户放置公式的GUI生成的,最终用户会出错。因此,为了向他们澄清错误意味着什么,我们想给他们造成错误的记录的id。啊。那么我唯一能建议的就是删除“saveexceptions”子句,在循环中放置一个try块,并在有行数据可用的地方处理异常。
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;

...