2016-05-31 25 views
5

Tôi nhận thấy hành vi lạ của NO_DATA_FOUND ngoại lệ khi được ném từ hàm được sử dụng trong PLSQL.Trường hợp ngoại lệ NO_DATA_FOUND không được ném khi được sử dụng trong SELECT INTO

Ngắn câu chuyện - nó truyền từ chức năng khi sử dụng chuyển nhượng và không lan truyền (hoặc được xử lý âm thầm ở đâu đó ở giữa) khi được sử dụng trong SELECT INTO.

Vì vậy, chức năng trao test_me ném NO_DATA_FOUND ngoại lệ, khi viện dẫn như:

v_x := test_me(p_pk); 

Nó ném một ngoại lệ, trong khi khi viện dẫn như:

SELECT test_me(p_pk) INTO v_x FROM dual; 

nó không ném ngoại lệ. Điều này không xảy ra với các trường hợp ngoại lệ khác. Dưới đây bạn có thể tìm thấy các ví dụ thử nghiệm của tôi.

Ai đó có thể giải thích cho tôi về hành vi này?

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

Ví dụ 1 không thành công trên 'ORA-01476: ước số bằng 0 ngoại lệ. – MT0

+0

Và nó có. :) Tôi đã thêm mô tả rõ ràng hơn về các ví dụ. –

Trả lời

6

Một ví dụ nhỏ là:

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

Nếu bạn làm:

SELECT raise_exception 
FROM DUAL; 

Bạn sẽ nhận được một hàng duy nhất chứa một giá trị NULL - Ask Tom trạng thái:

nó luôn luôn là như vậy

và sau đó theo dõi với:

no_data_found không phải là một lỗi - đó là một "điều kiện đặc biệt". Bạn, lập trình viên, quyết định nếu một cái gì đó là một lỗi bằng cách bắt các điều kiện đặc biệt và xử lý nó (làm cho nó được "không phải là một lỗi") hoặc bỏ qua nó (làm cho nó là một lỗi).

trong sql, không tìm thấy dữ liệu nào đơn giản có nghĩa là "không tìm thấy dữ liệu", dừng lại.

Trong trang bìa, SQL đang nâng cấp trở lại ứng dụng khách "hey buddy - no_data_found". Các khách hàng trong trường hợp này nói "ah hah, không tìm thấy dữ liệu có nghĩa là 'kết thúc dữ liệu'" và dừng lại.

Vì vậy, ngoại lệ được nêu trong hàm và máy khách SQL thấy điều này và diễn giải điều này vì không có dữ liệu nào là giá trị NULL và "xử lý" ngoại lệ.

Vì vậy

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

sẽ thành công như bảng DUAL có một hàng duy nhất và các ngoại lệ từ chức năng sẽ bị xử lý (âm thầm) và biến sẽ kết thúc chứa một giá trị NULL.

Tuy nhiên,

BEGIN 
    DBMS_OUTPUT.PUT_LINE(raise_exception); 
END; 
/

Trường hợp ngoại lệ là thời gian này được truyền từ hàm số tới một phạm vi PL/SQL - mà không xử lý các lỗi và vượt qua ngoại lệ đối với khối xử lý ngoại lệ (mà không tồn tại) sau đó được chuyển đến phạm vi ứng dụng và chấm dứt thực hiện chương trình.

Và Hỏi Tom nói:

Dưới sự bao trùm, PLSQL được nâng cao về ứng dụng khách hàng "hey - no_data_found Các khách hàng trong trường hợp này nói" uh-oh, không ngờ rằng từ PLSQL - sql chắc chắn, nhưng không phải PLSQL. Cho phép in ra văn bản đi kèm với điều kiện đặc biệt này và tiếp tục trên "

Bạn thấy - đó là tất cả trong cách CLIENT diễn giải thông điệp ORA-xxxxx. Thông điệp đó, khi được nâng lên bởi SQL, được giải thích bởi Thông điệp đó, khi được PLSQL đưa ra và không được lập trình PLSQL xử lý, mặt khác được hiểu là "một điều xấu vừa xảy ra"

Cả PLSQL và SQL đều thực sự làm điều tương tự Đây là KHÁCH HÀNG đang quyết định làm điều gì đó khác biệt.

Bây giờ, nếu chúng ta thay đổi chức năng để tăng một ngoại lệ khác:

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

Sau đó, cả hai:

SELECT raise_exception 
FROM DUAL; 

và:

BEGIN 
    DBMS_OUTPUT.PUT_LINE(raise_exception); 
END; 
/

không biết làm thế nào để xử lý các ngoại lệ và chấm dứt với ORA-01476 divisor is equal to zero.

+0

Ok, đó là suy nghĩ của tôi ban đầu giống nhau - no_data_found được xử lý ở đâu đó âm thầm. Điều đó sẽ giải thích làm việc như được chọn từ SQL Developer chẳng hạn. Nhưng tôi không thấy sự thay đổi của CLIENT giữa select into và assignent khi cả hai đều được thực hiện từ PLSQL ... Hoặc có thể SELECT INTO gọi SQL thông dịch riêng biệt xử lý ngoại lệ, và trả về kết quả trở lại vào PLSQL? –

+0

@ TomaszZieleśkiewicz Bài tập BEGIN BEGIN v_x: = RAISE_EXCEPTION; END; 'được hiểu hoàn toàn trong PL/SQL. Mã 'BEGIN SELECT RAISE_EXCEPTION INTO v_x FROM DUAL; END; 'bắt đầu được diễn dịch trong PL/SQL và sau đó chuyển ngữ cảnh khi nó phân tích cú pháp câu lệnh' SELECT' được diễn giải trong SQL - khi câu lệnh được phân tích cú pháp các biến được đặt bởi mệnh đề 'INTO' được truyền qua lại giữa các phạm vi đến PL/SQL. – MT0

+0

Ok, sau đó nó làm cho một số ý nghĩa. Nhưng tôi phải thừa nhận rằng nó vẫn cảm thấy một chút không phù hợp với tôi. ;) Cảm ơn bạn đã trả lời của bạn, nó là hữu ích nhất. –

Các vấn đề liên quan