我有一个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语句,当然因光标关闭而失败。有可能吗?
答案 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;
...