引发异常的范围,在 PLSQL 代码中处理自己的异常
Posted
技术标签:
【中文标题】引发异常的范围,在 PLSQL 代码中处理自己的异常【英文标题】:scope of raise exception, handling your own exceptions in PLSQL code 【发布时间】:2010-01-22 09:45:06 【问题描述】:我有这个程序:
create or replace PROCEDURE CONVERTE
IS
CURSOR oldemployees IS
SELECT *
FROM emp1
WHERE data_saida= NULL;
new_ndep emp1.num_dep%type;
bi_inexistente EXCEPTION;
dep_inexistente EXCEPTION;
employeeNr emp1.num_empregado%type;
BEGIN
FOR old_emp IN oldemployees
LOOP
employeeNr:= old_emp.num_empregado;
if (old_emp.bi = NULL) then
raise bi_inexistente;
else
IF (old_emp.num_dep>20) THEN
SELECT ndep_novo INTO new_ndep FROM Converte_dep WHERE ndep_antigo= old_emp.num_dep;
elsif (old_emp.num_dep = NULL) then
new_ndep:= 0;
raise dep_inexistente;
end if;
INSERT INTO EMP2 VALUES (old_emp.bi, old_emp.nome, old_emp.morada, old_emp.data_entrada, old_emp.data_saida, new_ndep);
COMMIT;
end if;
end loop;
EXCEPTION
when bi_inexistente then
INSERT INTO ERROS VALUES(employeeNr, 'BI Inexistente');
COMMIT;
when dep_inexistente then
INSERT INTO ERROS VALUES(employeeNr, 'Departamento Inexistente');
COMMIT;
end;
我想做 INSERT INTO EMP2 VALUES (old_emp.bi, old_emp.nome, old_emp.morada, old_emp.data_entrada, old_emp.data_saida, new_ndep);即使在提高dep_inexistente之后,但是在阅读了oracle的参考之后,我还是有点困惑;基本上,当它为空时,我不想插入,否则我想插入,即使部门号为空(我转为 0)。
那么,代码是否正确,或者我应该如何引发我的异常或为我的案例处理预定义的异常?
【问题讨论】:
【参考方案1】:我想做 INSERT INTO EMP2 VALUES (old_emp.bi,old_emp.nome, old_emp.morada, old_emp.data_entrada, old_emp.data_saida, new_ndep);甚至 后提升dep_inexistente
诀窍是在进行插入之后 引发该异常。引发的异常实际上是 GOTO
语句 - 控制流直接压缩到 EXCEPTIONS 块。在下面的重写中,我使用你的 new_dep 设置作为引发异常的信号。您可能知道其他一些使这种方法无效的业务逻辑(即,记录的部门为零是有某些正当理由的)。在这种情况下,您需要设置一个标志。
create or replace PROCEDURE CONVERTE IS
CURSOR oldemployees IS
SELECT *
FROM emp1
WHERE data_saida= NULL;
new_ndep emp1.num_dep%type;
bi_inexistente EXCEPTION;
dep_inexistente EXCEPTION;
employeeNr emp1.num_empregado%type;
BEGIN
FOR old_emp IN oldemployees
LOOP
employeeNr:= old_emp.num_empregado;
if (old_emp.bi is NULL) then
raise bi_inexistente;
else
if (old_emp.num_dep>20) THEN
SELECT ndep_novo INTO new_ndep FROM Converte_dep WHERE ndep_antigo= old_emp.num_dep;
elsif (old_emp.num_dep is NULL) then
new_ndep:= 0;
end if;
INSERT INTO EMP2 VALUES (old_emp.bi, old_emp.nome, old_emp.morada, old_emp.data_entrada, old_emp.data_saida, new_ndep);
COMMIT;
if new_ndep = 0 then
raise dep_inexistente;
end if;
end if;
end loop;
EXCEPTION
when bi_inexistente then
INSERT INTO ERROS VALUES(employeeNr, 'BI Inexistente');
COMMIT;
when dep_inexistente then
INSERT INTO ERROS VALUES(employeeNr, 'Departamento Inexistente');
COMMIT;
end;
关于您的一般方法的三件事:
-
任何异常都会使 LOOP 短路。不会再处理更多行
因为您是在 LOOP 中提交的,所以可能很难重新运行该程序,因为您将无法轻松地从中断的地方继续。
在循环内提交可能会产生 ORA-1555 或 ORA-1002 错误问题,尤其是当这是一个长时间运行的查询时。
编辑
实际上,您的代码引发了很多关于程序逻辑的问题。远远超过我想进入这里。我在上面列出的三个是一般的“最佳实践”问题,但条件流的详细逻辑看起来很可疑。但是我不知道您正在实施的业务规则。
【讨论】:
【参考方案2】:我认为不应将异常用作不雅的 GOTO 语句。如果你想构建你的代码,你可以使用过程(和子过程)。如果工作在代码中的某个位置完成,只需使用 RETURN 语句。仅在有意义时才捕获异常。
【讨论】:
【参考方案3】:你的代码有错误:old_emp.num_dep = NULL
不能工作,它总是假的。
假设它是old_emp.num_dep IS NULL
,那么我认为你的代码不会按照你的意图工作。绕过 INSERT INTO EMP2 会引发异常。
如果这是我的代码,并且逻辑是这样的,您可以确定在缺少部门的情况下插入 EMP2 不是真正的错误,我不会引发异常。您也不会丢失该信息,因为您总是可以看到缺少部门(即每个部门都为 0 的 emp)
顺便说一句,部门使用 0 有什么特殊原因吗?为什么不直接使用NULL
?显然你已经决定让员工没有部门是可以的,NULL
就是一个公平的代表。
如果你坚持认为 emp 错过部门实际上是一个错误,但仍然觉得插入 EMP 是可以的,我会考虑这样写:
IF ... THEN
... -- ok
END IF;
INSERT INTO EMP2 VALUES (
old_emp.bi, old_emp.nome, old_emp.morada, old_emp.data_entrada, old_emp.data_saida,
NVL(new_ndep, 0)
);
IF new_ndep IS NULL THEN
raise dep_inexistente;
END IF;
我强烈建议您在代码中添加一些 cmets,因为如果我能找到我上面写的代码,我可能会怀疑存在错误。
【讨论】:
【参考方案4】:所以,如果我保留异常,它会是这样的:
create or replace PROCEDURE CONVERTE IS
CURSOR oldemployees IS
SELECT *
FROM emp1
WHERE data_saida= NULL;
new_ndep emp1.num_dep%type;
bi_inexistente EXCEPTION;
dep_inexistente EXCEPTION;
employeeNr emp1.num_empregado%type;
BEGIN
FOR old_emp IN oldemployees
LOOP
employeeNr:= old_emp.num_empregado;
if (old_emp.bi is NULL) then
raise bi_inexistente;
else
if (old_emp.num_dep>20) THEN
SELECT ndep_novo INTO new_ndep FROM Converte_dep WHERE ndep_antigo= old_emp.num_dep;
else
INSERT INTO EMP2 VALUES (old_emp.bi, old_emp.nome, old_emp.morada, old_emp.data_entrada, old_emp.data_saida,nvl(old_emp.num_dep,0));
end if;
if new_ndep is NULL then
raise dep_inexistente;
end if;
end if;
end loop;
EXCEPTION
when bi_inexistente then
INSERT INTO ERROS VALUES(employeeNr, 'BI Inexistente');
COMMIT;
when dep_inexistente then
INSERT INTO ERROS VALUES(employeeNr, 'Departamento Inexistente');
COMMIT;
end;
或者我可以只做被告知的事情,而不引发异常;但我仍然必须使用光标。
create or replace
PROCEDURE CONVERTE2 IS
CURSOR oldemployees IS
SELECT *
FROM emp1
WHERE data_saida= NULL;
new_ndep emp1.num_dep%type;
bi_inexistente EXCEPTION;
dep_inexistente EXCEPTION;
employeeNr emp1.num_empregado%type;
v_error_code NUMBER:=0;
v_error_message VARCHAR2(255);
BEGIN
FOR old_emp IN oldemployees
LOOP
employeeNr:= old_emp.num_empregado;
if (old_emp.bi is NULL) then
INSERT INTO ERROS VALUES(employeeNr, 'BI Inexistente');
else
if (old_emp.num_dep>20) THEN
SELECT ndep_novo INTO new_ndep FROM Converte_dep WHERE ndep_antigo= old_emp.num_dep;
else
INSERT INTO EMP2 VALUES (old_emp.bi, old_emp.nome, old_emp.morada, old_emp.data_entrada, old_emp.data_saida,nvl(old_emp.num_dep,0));
end if;
if new_ndep is NULL then
INSERT INTO ERROS VALUES(employeeNr, 'Departamento Inexistente');
end if;
end if;
end loop;
COMMIT;
EXCEPTION
When others Then
ROLLBACK;
/*eventually log something into erro table*/
end;
那么你会如何重写它,让它看起来不那么“不确定”?这有点乱,我不得不承认。不管怎样,至少你给了我非常实用的见解。 我想看看一些更好的方法,如果你愿意,我很感兴趣。
【讨论】:
以上是关于引发异常的范围,在 PLSQL 代码中处理自己的异常的主要内容,如果未能解决你的问题,请参考以下文章
我在12。21要去一家软件公司笔试,范围大约是VB,JAVA方面的,劳驾帮忙给点相关题目。