2012-01-18 10 views
10

Tôi đang cố gắng thực hiện một số kiểm tra trên lược đồ DB ở đầu tập lệnh PL/SQL.PL/SQL: có hướng dẫn để dừng hoàn toàn việc thực thi tập lệnh không?

Nếu kiểm tra cho kết quả không thành công, tôi muốn dừng tập lệnh để ngăn các lệnh tiếp theo được thực hiện.

tôi có một cái gì đó như thế này

-- 1st line of PL/SQL script 

DECLARE 
    SOME_COUNT INTEGER; 
BEGIN 
    SELECT COUNT(*) INTO SOME_COUNT FROM SOME_TABLE WHERE <SOME_CONDITIONS>; 
    IF (SOME_COUNT > 0) THEN 
    DBMS_OUTPUT.PUT_LINE('Test failed, I don''want the rest of the script' 
     || ' to be executed.'); 
    --EXIT or something like that?... <= STOP EXECUTION HERE 
    END IF; 
END; 
/

-- OTHER SQL INSTRUCTIONS... 
ALTER TABLE SOME_TABLE ... 

Tôi đang tìm kiếm các hướng dẫn (s) cho phép làm "STOP EXECUTION HERE". hơn

+5

Bạn có thực sự đang nói về một tập lệnh PL/SQL không? Hoặc một kịch bản lệnh SQL * Plus? Bạn không thể có một câu lệnh DDL trong PL/SQL (trừ khi bạn đặt nó trong một 'EXECUTE IMMEDIATE'). Vì vậy, nó xuất hiện với tôi rằng bạn đang nói về một kịch bản SQL * Plus. Nếu bạn đang nói về một kịch bản lệnh SQL * Plus, nếu khối PL/SQL bị lỗi, SQL * Plus sẽ mặc định tiếp tục thực hiện câu lệnh SQL tiếp theo (hoặc khối PL/SQL) trong kịch bản lệnh. Bạn sẽ cần sử dụng lệnh SQL * Plus 'WHENEVER SQLERROR EXIT'. –

+0

Cảm ơn bạn đã bình luận của bạn Justin. Trong thực tế, tôi không phải là một "chuyên gia Oracle", ngay cả khi tôi có kiến ​​thức tốt về DBMS nói chung. Vì vậy, tôi chưa nắm được sự khác biệt giữa các kịch bản lệnh PL/SQL hoặc SQL * Plus (bạn có biết một số blog/doc/trang web giải thích rõ ràng sự khác biệt đó không?). Tất cả những gì tôi có thể nói là tôi đang sử dụng ** Navicat **, sử dụng cửa sổ "truy vấn" và tải tệp kịch bản của tôi vào đó. Trong bối cảnh cụ thể đó, 'RAISE_APPLICATION_ERROR()' thực hiện công việc. –

Trả lời

9

Dựa trên câu hỏi, tôi không đồng ý với câu trả lời được chấp nhận. Câu hỏi hiển thị một kịch bản lệnh nhiều câu lệnh. RAISE_APPLICATION_ERROR() chỉ thoát ra khỏi một khối PL/SQL (chương trình con), không nằm ngoài tập lệnh tổng thể (như được chỉ ra bởi Justin) vì vậy nó sẽ tiếp tục với các câu lệnh tiếp theo.

Đối với tập lệnh theo lô, tốt nhất bạn nên sử dụng WHENEVER SQLERROR EXIT. Vâng, đó là một chỉ thị SQL * Plus, không phải là SQL chuẩn, nhưng khá là di động; các công cụ Oracle phổ biến nhất hỗ trợ các tập lệnh hỗ trợ chỉ thị này, ít nhất một phần. Ví dụ sau đây hoạt động trong SQL * Plus, SQL * Developer, Toad, SQLsmith và có thể là những người khác, và thể hiện vấn đề, nếu bạn bình luận dòng đó.

set serveroutput on 

-- Without this line, things keep going 
WHENEVER SQLERROR EXIT SQL.SQLCODE ROLLBACK; 

BEGIN 
    IF (1 > 0) THEN 
    DBMS_OUTPUT.PUT_LINE('First thing'); 
    RAISE_APPLICATION_ERROR(-20000, 'Test failed'); -- not enough 
    END IF; 
END; 
/

-- This will execute if you remove WHEN SQLERROR.., so RAISE_APPLICATION_ERROR is not enough 
BEGIN 
    DBMS_OUTPUT.PUT_LINE('Second thing - Executes anyway'); 
END; 
/

Nếu bạn loại bỏ WHEN SQLERROR, tập lệnh sẽ tiếp tục và thực thi khối thứ 2, v.v ... chính xác là câu hỏi cần tránh. Lợi ích, trong trường hợp này, các công cụ đồ họa mô phỏng sqlplus, là chúng thực sự dừng kịch bản và không gửi phần còn lại của kịch bản lệnh shell như lệnh shell, đó là những gì sẽ xảy ra nếu bạn dán tập lệnh vào SQL * Plus đang chạy trong cửa sổ bảng điều khiển. SQL * Plus có thể thoát do lỗi, nhưng các lệnh đệm còn lại sau đó sẽ được xử lý bởi vỏ hệ điều hành, một chút lộn xộn và có khả năng nguy hiểm, nếu bạn có lệnh shell trong các chú thích (không phải là chưa từng nghe). Với SQLPlus, nó luôn luôn là tốt nhất để kết nối, và sau đó thực thi kịch bản, hoặc vượt qua nó trong đối số dòng lệnh < start (sqlplus scott/tiger @ foo.sql) để tránh điều này.

+0

Cảm ơn câu trả lời chu đáo của bạn. –

+0

+1 điều này thực sự hữu ích. Tôi biết về ngoại lệ và lỗi ứng dụng nhưng tôi cần thực hiện một kiểm tra điều kiện tiên quyết trong tập lệnh DDL nên dừng tập lệnh. Việc thay thế (làm DDL trong một câu lệnh IF trong một kịch bản PL/SQL) không phải là rất hấp dẫn. –

5

Một vài giây googling đã cho tôi câu trả lời: chức năng RAISE_APPLICATION_ERROR()

IF (SOME_COUNT > 0) THEN 
    RAISE_APPLICATION_ERROR(-20000, 'Test failed'); 
    END IF; 

Mã lỗi người dùng định nghĩa nên được giữa -20.000 -20.999 và.

Chi tiết về Oracle doc ở đây: http://docs.oracle.com/cd/B10501_01/appdev.920/a96624/07_errs.htm#877 (phần Xác định riêng của bạn thông báo lỗi: Thủ tục RAISE_APPLICATION_ERROR)

+2

Nếu bạn sử dụng 'raise_application_error' trong mã mà bạn đã đăng trong câu hỏi của mình, nó sẽ tách bạn ra khỏi khối PL/SQL nhưng phần' CÁC HƯỚNG DẪN SQL KHÁC 'sẽ vẫn được thực hiện. Đây là những gì bạn đang sau? –

7

Nếu bạn không muốn nâng một ngoại lệ, bạn có thể thử một cái gì đó tương tự (chưa được kiểm tra):

declare 
    SOME_COUNT INTEGER; 
begin 
    SELECT COUNT(*) INTO SOME_COUNT FROM SOME_TABLE WHERE <SOME_CONDITIONS>; 
    IF (SOME_COUNT > 0) THEN 
    DBMS_OUTPUT.PUT_LINE('Test failed, I don''want the rest of the script' 
     || ' to be executed.'); 

    goto end_proc; 
    END IF; 

    -- A bunch of great code here 

    <<end_proc>> 
    null; -- this could be a commit or other lines of code 
end; 

Một số người ghét bất kỳ câu lệnh GOTO nào vì chúng có thể dẫn đến mã spaghetti nếu bị lạm dụng, nhưng trong những tình huống đơn giản như thế này (một lần nữa, giả sử bạn không muốn nêu một ngoại lệ) chúng hoạt động tốt.

+1

Cảm ơn, +1. Vấn đề duy nhất tôi thấy, là tôi đã có rất nhiều câu lệnh DDL ('ALTER TABLE' ...) là' bó mã lớn', đòi hỏi phải sử dụng nhiều cách giải quyết 'EXECUTE IMMEDIATE', do đó mã kịch bản sẽ ít có thể đọc/duy trì được. –

+0

Vâng, đó là một vấn đề khác với OP của bạn, nhưng tôi đồng ý rằng một số mã ra có xa "tuyệt vời" ;-) Nhận xét đó là một chút lưỡi trong má, nhưng một lần nữa điểm là đưa ra một giải pháp thay thế cho câu hỏi của bạn . – tbone

1

Thay vì ném lỗi ứng dụng, việc sử dụng RETURN keyword đơn giản hơn sẽ thoát khỏi khối PL/SQL hiện tại rất thuận lợi.

Chỉ cần đảm bảo bạn thực hiện DBMS_OUTPUT.PUT_LINE('Exited because <error') trước khi nó cung cấp cho người dùng một thông điệp hay về lý do bạn thoát khỏi khóa học!