在SELECT INTO中使用时,不会抛出NO_DATA_FOUND异常

时间:2016-05-31 11:39:49

标签: sql oracle plsql oracle11g exception-handling

我注意到从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;

1 个答案:

答案 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终止。