2012-01-18 19 views
10

Próbuję zrobić kilka sprawdzeń na schemacie DB na początku skryptu PL/SQL.PL/SQL: czy istnieje instrukcja, aby całkowicie zatrzymać wykonywanie skryptu?

Jeśli wyniki nie przyniosą rezultatu, chcę zatrzymać skrypt, aby uniemożliwić wykonanie następnych instrukcji.

Mam coś jak ten

-- 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 ... 

szukam instrukcji (ów) pozwalający zrobić „STOP EXECUTION HERE”.

+5

Czy naprawdę mówisz o skrypcie PL/SQL? Lub skrypt SQL * Plus? Nie możesz mieć instrukcji DDL w PL/SQL (chyba że umieścisz ją w "WYKONAJ NATYCHMIAST"). Wydaje mi się, że mówisz o skrypcie SQL * Plus. Jeśli mówimy o skrypcie SQL * Plus, jeśli błędy bloku PL/SQL się nie powiedzie, SQL * Plus domyślnie będzie kontynuował wykonywanie w skrypcie następnej instrukcji SQL (lub bloku PL/SQL). Będziesz musiał użyć polecenia SQL * Plus 'WHENEVER SQLERROR EXIT'. –

+0

Dzięki za komentarz Justin. W rzeczywistości nie jestem "ekspertem Oracle", nawet jeśli mam dobrą znajomość DBMS w ogóle. W związku z tym nie zrozumiałem jeszcze różnicy pomiędzy skryptami PL/SQL lub SQL * Plus (znasz bloga/doc/stronę wyjaśniającą wyraźnie różnicę?). Wszystko, co mogę powiedzieć, to to, że używam ** Navicat **, używając okienka "zapytanie" i ładuję do niego mój plik skryptu. W tym konkretnym kontekście '' RAISE_APPLICATION_ERROR() 'wykonuje zadanie. –

Odpowiedz

9

Na podstawie tego pytania nie zgadzam się z zaakceptowaną odpowiedzią. Pytanie pokazuje skrypt wsadowy z wieloma instrukcjami. Funkcja RAISE_APPLICATION_ERROR() wychodzi tylko z bloku PL/SQL (podprogramu), a nie z całego skryptu (jak zauważył Justin), więc będzie kontynuowana z następującymi instrukcjami.

W przypadku skryptów wsadowych najlepiej jest użyć WHENEVER SQLERROR EXIT. Tak, jest to dyrektywa SQL * Plus, a nie standardowy SQL, ale jest dość przenośna; najpopularniejsze narzędzia Oracle obsługujące skrypty obsługują tę dyrektywę, przynajmniej częściowo. Poniższy przykład działa w SQL * Plus, SQL * Developer, Toad, SQLsmith i prawdopodobnie innych, i demonstruje problem, jeśli skomentujesz linię.

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; 
/

Jeśli usunąć KIEDY SQLERROR, skrypt będzie kontynuować i wykonać 2nd blok, itp, które to właśnie pytanie zadaje się uniknąć.

Zaletą w tym przypadku narzędzi graficznych emulujących sqlplus jest to, że naprawdę zatrzymują skrypt i nie przekazują pozostałej części skryptu do powłoki poleceń jako polecenia powłoki, co dzieje się po wklejeniu skrypty do SQL * Plus uruchomione w oknie konsoli. SQL * Plus może wyjść z błędem, ale pozostałe buforowane polecenia będą obsługiwane przez powłokę systemu operacyjnego, która jest nieco chaotyczna i potencjalnie ryzykowna, jeśli w komentarzach byłyby polecenia powłoki (co nie jest niespotykane). Z SQLPlus zawsze najlepiej jest połączyć się, a następnie wykonać skrypt lub przekazać go w argumencie linii poleceń < start (sqlplus scott/tiger @ foo.sql), aby tego uniknąć.

+0

Dziękuję za twoją przemyślaną odpowiedź. –

+0

+1 to naprawdę pomogło. Wiedziałem o wyjątkach i błędach aplikacji, ale muszę wykonać jedno sprawdzanie warunków wstępnych w skrypcie DDL, który powinien zatrzymać skrypt. Alternatywa (wykonanie DDL w instrukcji IF w skrypcie PL/SQL) nie jest zbyt kusząca. –

5

Jeszcze kilka sekund googlowania dał mi odpowiedź: funkcję RAISE_APPLICATION_ERROR()

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

Kod błędu zdefiniowany przez użytkownika powinna wynosić od -20000 i -20999.

Szczegóły dotyczące Oracle doc tutaj: http://docs.oracle.com/cd/B10501_01/appdev.920/a96624/07_errs.htm#877 (sekcja Definiowanie własnych komunikatów o błędach: Procedura RAISE_APPLICATION_ERROR)

+2

Jeśli użyjesz 'raise_application_error' w kodzie, który opublikowałeś w swoim pytaniu, to złamie cię z bloku PL/SQL, ale sekcja" INNE SQL INSTRUCTIONS "nadal będzie wykonywana. Czy tego właśnie szukasz? –

7

Jeśli nie chcesz, aby podnieść wyjątek, można spróbować czegoś podobnego (niesprawdzone):

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; 

Niektórzy ludzie nienawidzą żadnych instrukcji GOTO, ponieważ mogą prowadzić do złamania kodu spaghetti, ale w prostych sytuacjach takich jak ten (ponownie, zakładając, że nie chcesz podnieść wyjątku), działają dobrze.

+1

Dzięki, +1. Jedyny problem, jaki widzę, to to, że mam wiele instrukcji DDL ("ALTER TABLE" ...) jako "pęczek świetnego kodu", wymagające dużo użycia rozwiązania "WYKONAJ NATYCHMIAST", w związku z czym kod skryptu być mniej czytelne/możliwe do utrzymania. –

+0

Tak, to jest inny problem niż twój OP, ale zgadzam się, że jakiś kod tam jest daleki od "wielkiego" ;-) Ten komentarz był nieco językiem w policzek, ale znowu chodziło o to, aby dać alternatywne rozwiązanie dla twojego pytania . – tbone

1

Zamiast wyrzucać błąd aplikacji, znacznie łatwiej jest po prostu użyć RETURN keyword, który bardzo płynnie wychodzi z aktualnego bloku PL/SQL.

Tylko upewnij się, że zrobisz przed sobą DBMS_OUTPUT.PUT_LINE('Exited because <error'), aby zapewnić użytkownikowi ładną wiadomość, dlaczego wychodzisz z kursu!

Powiązane problemy