我注意到从PLSQL中使用的函数抛出时NO_DATA_FOUND
异常的奇怪行为。
长话短说 - 它在使用赋值时从函数传播,并且在SELECT INTO
中使用时不会传播(或在两者之间的某处静默处理)。
因此,给定函数test_me
抛出NO_DATA_FOUND
异常,当调用为:
v_x := test_me(p_pk);
抛出异常,而调用时为:
SELECT test_me(p_pk) INTO v_x FROM dual;
它不会抛出异常。其他例外情况不会发生这种情况。下面您可以找到我的测试示例。
有人可以向我解释一下这种行为吗?
set serveroutput on;
CREATE OR REPLACE FUNCTION test_me(p_pk NUMBER) RETURN NVARCHAR2
IS
v_ret NVARCHAR2(50 CHAR);
BEGIN
BEGIN
SELECT 'PYK' INTO v_ret FROM dual WHERE 1 = 1/p_pk;
EXCEPTION WHEN NO_DATA_FOUND THEN
dbms_output.put_line(chr(9)||chr(9)||chr(9)||' (test_me NO_DATA_FOUND handled and rerised)');
RAISE;
END;
RETURN v_ret;
END;
/
DECLARE
v_x NVARCHAR2(500 CHAR);
v_pk NUMBER;
PROCEDURE test_example(p_pk NUMBER)
IS
BEGIN
BEGIN
dbms_output.put_line(chr(9)||chr(9)||'Test case 1: Select into.');
SELECT test_me(p_pk) INTO v_x FROM dual;
dbms_output.put_line(chr(9)||chr(9)||'Success: '||NVL(v_x,'NULL RETURNED'));
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line(chr(9)||chr(9)||'Failure: NO_DATA_FOUND detected');
WHEN OTHERS THEN
dbms_output.put_line(chr(9)||chr(9)||'Failure: '||SQLCODE||' detected');
END;
dbms_output.put_line(' ');
BEGIN
dbms_output.put_line(chr(9)||chr(9)||'Test case 2: Assignment.');
v_x := test_me(p_pk);
dbms_output.put_line(chr(9)||chr(9)||'Success: '||NVL(v_x,'NULL RETURNED'));
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line(chr(9)||chr(9)||'Failure: NO_DATA_FOUND detected');
WHEN OTHERS THEN
dbms_output.put_line(chr(9)||chr(9)||'Failure: '||SQLCODE||' detected');
END;
END;
BEGIN
dbms_output.put_line('START');
dbms_output.put_line(' ');
dbms_output.put_line(chr(9)||'Example 1: Function throws some exception, both cases throws exception, everything is working as expected.');
test_example(0);
dbms_output.put_line(' ');
dbms_output.put_line(chr(9)||'Example 2: Query returns row, there is no exceptions, everything is working as expected.');
test_example(1);
dbms_output.put_line(' ');
dbms_output.put_line(chr(9)||'Example 3: Query inside function throws NO_DATA_FOUND, strange things happen - one case is throwing exception, the other is not.');
test_example(2);
dbms_output.put_line(' ');
dbms_output.put_line('END');
END;
/
DROP FUNCTION test_me;
答案 0 :(得分:6)
最小的例子是:
CREATE FUNCTION raise_exception RETURN INT
IS
BEGIN
RAISE NO_DATA_FOUND;
END;
/
如果你这样做:
SELECT raise_exception
FROM DUAL;
您将获得包含NULL
值的单行 - Ask Tom个州:
它一直都是这样
然后跟进:
no_data_found不是错误 - 它是一个特殊情况"。程序员通过捕获异常条件并处理它(使其成为"不是错误")或忽略它(使其成为错误)来决定是否存在错误。
在sql中,没有发现数据非常简单意味着"没有找到数据",停止。
在幕后,SQL正在回到客户端应用程序"嘿伙计 - no_data_found"。该 在这种情况下,客户说"啊哈,没有发现数据意味着数据结束'"并停止。
因此在函数中引发了异常,SQL客户端看到了这个异常,并将其解释为没有NULL
值的数据和"处理"例外。
所以
DECLARE
variable_name VARCHAR2(50);
BEGIN
SELECT raise_exception
INTO variable_name
FROM DUAL
END;
/
将成功,因为DUAL
表有一行,并且函数的异常将被处理(静默),变量最终将包含NULL
值。
然而,
BEGIN
DBMS_OUTPUT.PUT_LINE( raise_exception );
END;
/
此异常是从函数传递到PL / SQL作用域 - 它不处理错误并将异常传递给异常处理程序块(不存在),然后传递给应用程序作用域并终止程序的执行。
问汤姆说:
在幕后,PLSQL正在回到客户端应用程序"嘿 - no_data_found。在这种情况下,客户说"呃 - 哦,不是期望来自PLSQL - sql肯定,而不是PLSQL。让我们打印出符合这种特殊情况的文本,然后继续"
你知道 - 这完全取决于CLIENT解释ORA-xxxxx消息的方式。当由SQL引发时,该消息被客户端解释为"您已完成"。另一方面,当PLSQL引发而不是由PLSQL程序员处理时,该消息被解释为"发生了一件坏事"
PLSQL和SQL实际上都在这里做同样的事情。客户决定采取不同的行动。
现在,如果我们更改函数以引发另一个异常:
CREATE OR REPLACE FUNCTION raise_exception RETURN INT
IS
BEGIN
RAISE ZERO_DIVIDE;
END;
/
然后两个:
SELECT raise_exception
FROM DUAL;
和
BEGIN
DBMS_OUTPUT.PUT_LINE( raise_exception );
END;
/
不知道如何处理异常并以ORA-01476 divisor is equal to zero
终止。