简体   繁体   English

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

[英]NO_DATA_FOUND exception not thrown when used in SELECT INTO

I noticed strange behaviour of NO_DATA_FOUND exception when thrown from function used in PLSQL. 从PLSQL中使用的函数抛出时,我注意到NO_DATA_FOUND异常的奇怪行为。

Long story short - it does propagate from function when using assignment, and does not propagate (or is handled silently somewhere in between) when used in SELECT INTO . 长话短说 - 它在使用赋值时确实从函数传播,并且在SELECT INTO使用时不会传播(或在两者之间的某处静默处理)。

So, given function test_me throwing NO_DATA_FOUND exception, when invoked as: 因此,给定函数test_me抛出NO_DATA_FOUND异常,当调用为:

v_x := test_me(p_pk);

It throws an exception, while when invoked as: 它会引发异常,而在调用时:

SELECT test_me(p_pk) INTO v_x FROM dual;

it does not throw exception. 它不会抛出异常。 This does not occur with other exceptions. 其他例外情况不会发生这种情况。 Below You can find my test examples. 下面您可以找到我的测试示例。

Could somebody please explain to me this behaviour? 有人可以向我解释一下这种行为吗?

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;

A minimal example is: 一个最小的例子是:

CREATE FUNCTION raise_exception RETURN INT
IS
BEGIN
  RAISE NO_DATA_FOUND;
END;
/

If you do: 如果你这样做:

SELECT raise_exception
FROM   DUAL;

You will get a single row containing a NULL value - Ask Tom states: 您将获得包含NULL值的单行 - Ask Tom声明:

it has ALWAYS been that way 它总是这样

and then followed up with: 然后跟进:

no_data_found is not an error - it is an "exceptional condition". no_data_found不是错误 - 这是一个“例外情况”。 You, the programmer, decide if something is an error by catching the exceptional condition and handling it (making it be "not an error") or ignoring it (making it be an error). 程序员通过捕获异常条件并处理它(使其成为“非错误”)或忽略它(使其成为错误)来决定是否存在错误。

in sql, no data found quite simply means "no data found", stop. 在sql中,没有发现数据只是意味着“没有找到数据”,停止。

Under the covers, SQL is raising back to the client application "hey buddy -- no_data_found". 在幕后,SQL正在回到客户端应用程序“hey buddy - no_data_found”。 The client in this case says "ah hah, no data found means 'end of data'" and stops. 在这种情况下客户说“啊哈,没有发现数据意味着'数据结束'”并停止。

So the exception is raised in the function and the SQL client sees this and interprets this as there is no data which is a NULL value and "handles" the exception. 因此,在函数中引发了异常,SQL客户端看到了这一点,并将其解释为没有数据是NULL值并“处理”异常。

So 所以

DECLARE
  variable_name VARCHAR2(50);
BEGIN
  SELECT raise_exception
  INTO   variable_name
  FROM   DUAL
END;
/

Will succeed as the DUAL table has a single row and the exception from the function will be handled (silently) and the variable will end up containing a NULL value. 将成功,因为DUAL表有一行,并且函数的异常将被处理(静默),并且变量最终将包含NULL值。

However, 然而,

BEGIN
  DBMS_OUTPUT.PUT_LINE( raise_exception );
END;
/

The exception is this time being passed from the function to a PL/SQL scope - which does not handle the error and passes the exception to the exception handler block (which does not exist) so then gets passed up to the application scope and terminates execution of the program. 例外情况是从函数传递到PL / SQL作用域 - 它不处理错误并将异常传递给异常处理程序块(不存在),然后传递到应用程序作用域并终止执行该计划。

And Ask Tom states: 问汤姆说:

Under the covers, PLSQL is raising back to the client application "hey -- no_data_found. The client in this case says "uh-oh, wasn't expecting that from PLSQL -- sql sure, but not PLSQL. 在幕后,PLSQL正在重新回到客户端应用程序“嘿 - no_data_found。在这种情况下,客户端说”呃哦,不期望从PLSQL - sql肯定,但不是PLSQL。 Lets print out the text that goes with this exceptional condition and continue on" 让我们打印出符合这种特殊情况的文字并继续“

You see -- it is all in the way the CLIENT interprets the ORA-xxxxx message. 你看 - 它完全符合CLIENT解释ORA-xxxxx消息的方式。 That message, when raised by SQL, is interpreted by the client as "you are done". 当由SQL引发时,该消息被客户端解释为“你已经完成”。 That message, when raised by PLSQL and not handled by the PLSQL programmer, is on the other hand interpreted as "a bad thing just happened" 这条消息,当由PLSQL引发而不是由PLSQL程序员处理时,另一方面被解释为“发生了一件坏事”

Both PLSQL and SQL actually do the same thing here. PLSQL和SQL实际上都在这里做同样的事情。 It is the CLIENT that is deciding to do something different. 客户决定采取不同的做法。

Now, if we change the function to raise a different exception: 现在,如果我们更改函数以引发不同的异常:

CREATE OR REPLACE FUNCTION raise_exception RETURN INT
IS
BEGIN
  RAISE ZERO_DIVIDE;
END;
/

Then both: 然后两个:

SELECT raise_exception
FROM   DUAL;

and: 和:

BEGIN
  DBMS_OUTPUT.PUT_LINE( raise_exception );
END;
/

do not know how to handle the exception and terminate with ORA-01476 divisor is equal to zero . 不知道如何处理异常并终止ORA-01476 divisor is equal to zero

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM