Jak przerwać wykonywanie skryptu SQL

16

Pracuję nad skryptem SQL i wymagam, aby przestać kontynuować skrypt, jeśli niektóre warunki nie zostaną spełnione.

Po przejściu do Google znalazłem, że RaisError z poziomem 20 dotkliwości go zakończy. Ale z niektórych powodów nie mogę skorzystać z tej opcji.

Czy mogę podać, jakie są możliwe alternatywy, aby zatrzymać wykonywanie skryptu SQL.

Nowy programista
źródło
1
Dlaczego zgłaszanie błędu jest niedopuszczalne? Czy ten skrypt jest również procedurą składowaną?
Namphibian
Nie zrozumiałem jasno twojego pierwszego pytania. Na drugie pytanie; nie, to nie jest SP
New Developer
1
Co to jest skrypt? Czy obejmuje wiele partii? Widziałeś tutaj odpowiedzi?
Martin Smith,

Odpowiedzi:

8

Z dokumentacji RAISERROR ( wyróżnienie moje):

Poziomy ważności od 0 do 18 mogą być określone przez dowolnego użytkownika. Poziomy ważności od 19 do 25 mogą być określone tylko przez członków sysadmin stałej roli serwera lub użytkowników z uprawnieniami ALTER TRACE. Dla poziomów ważności od 19 do 25 wymagana jest opcja Z LOGEM.

Jest wysoce prawdopodobne, że zleceniodawca wykonuje skrypt, ponieważ nie spełnia tych kryteriów.

Nie ma nic złego w korzystaniu RAISERROR; po prostu używasz nadmiernego poziomu istotności. Używam poziomu 16 jako domyślnego dla zgłaszanego błędu i sekwencja zostanie zakończona. Jeśli chcesz być bardziej dokładny, możesz śledzić poziomy podane przez sam Microsoft:

wprowadź opis zdjęcia tutaj

Powiedziawszy to wszystko, w zależności od kontekstu skryptu, użycie RAISERRORmoże nie wystarczyć, ponieważ samo nie „wychodzi” ze skryptu (przy użyciu normalnych poziomów ważności).

Na przykład:

RAISERROR(N'Test', 16, 1);

SELECT 1;   /* Executed! */

Będzie to zarówno podnieść błąd i zwraca zestaw wyników.

Aby natychmiast zakończyć skrypt, wolę go używać RETURN(używanie GOTOkonstrukcji typu -type jest ogólnie odradzane w większości kręgów programistycznych, w których istnieją alternatywy):

RAISERROR(N'Test', 16, 1);
RETURN;

SELECT 1;   /* Not executed */

Lub obsłużyć błąd przy użyciu TRY/CATCH, co spowoduje przeskakiwanie wykonania do CATCHbloku, jeśli wskaźnik ważności wynosi 11 lub więcej:

BEGIN TRY
    RAISERROR(N'Test', 16, 1);
    SELECT 1;   /* Not executed */
END TRY
BEGIN CATCH
    SELECT 2;   /* Executed */
END CATCH

BEGIN TRY
    RAISERROR(N'Test', 10, 1);
    SELECT 1;   /* Executed */
END TRY
BEGIN CATCH
    SELECT 2;   /* Not executed */
END CATCH

Osobny problem polega na tym, że skrypt obejmuje wiele partii - zamknie RETURNtylko partię :

RAISERROR(N'Test', 16, 1);
RETURN;

SELECT 1;   /* Not executed */
GO

SELECT 2;   /* Executed! */

Aby to naprawić, możesz sprawdzić @@ERRORna początku każdej partii:

RAISERROR(N'Test', 16, 1);
RETURN;

SELECT 1;   /* Not executed */
GO

IF (@@ERROR != 0)
    RETURN;

SELECT 2;   /* Not executed */

Edycja: Jak słusznie zauważył Martin Smith w komentarzach, działa to tylko dla 2 partii. Aby rozszerzyć do 3 lub więcej partii, możesz kaskadowo zgłaszać błędy w ten sposób (uwaga: GOTOmetoda nie rozwiązuje tego problemu, ponieważ etykieta docelowa musi być zdefiniowana w partii):

RAISERROR(N'Test', 16, 1);
RETURN;

SELECT 1;   /* Not executed */
GO

IF (@@ERROR != 0)
BEGIN
    RAISERROR(N'Error already raised. See previous errors.', 16, 1);
    RETURN;
END

SELECT 2;   /* Not executed */
GO

IF (@@ERROR != 0)
BEGIN
    RAISERROR(N'Error already raised. See previous errors.', 16, 1);
    RETURN;
END

SELECT 3;   /* Not executed */

Lub, jak wskazuje również, możesz użyć tej SQLCMDmetody, jeśli jest to odpowiednie dla twojego środowiska.

Jon Seigel
źródło
Ta ostatnia sugestia nie działa. Zobacz pastebin . Podoba mi się tutaj metoda sqlcmd
Martin Smith,
6

Możesz użyć tego GOTOoświadczenia, aby przejść gdziekolwiek chcesz. Innymi słowy, napotkasz błąd lub jakiś inny warunek i możesz mieć etykietę na dole skryptu (tj. TheEndOfTheScript:) I po prostu wydać goto TheEndOfTheScript;instrukcję.

Oto krótka próbka:

print 'here is the first statement...';

print 'here is the second statement...';

-- substitute whatever conditional flow determining factor
-- you'd like here. I have chosen a dummy statement that will
-- always return true
--
if (1 = 1)
    goto TheEndOfTheScript;

print 'here is the third statement...';

print 'here is the fourth statement...';


TheEndOfTheScript:
print 'here is the end of the script...';

Dane wyjściowe tego wykonania będą następujące:

here is the first statement...
here is the second statement...
here is the end of the script...

Jak widać, GOTOpominął drukowanie trzeciej i czwartej instrukcji i skoczył w prawo do label ( TheEndOfTheScript).

Thomas Stringer
źródło
7
Działa tylko, gdy jest jedna partia, zepsuje się, gdy tylko pojawi się instrukcja GO.
Gabriel,
0

Zgadzam się z SET NOEXEC ON/OFF, jednak w przechowywanych procesach (zawierających pojedynczy blok) po prostu używam RETURNinstrukcji.

Ostrzeżenia: W pliku skryptu, jeśli masz wiele GOinstrukcji, RETURNwyjdą one tylko z bieżącego bloku i przejdą do następnego bloku / partii.

Uwaga: GOTOma to być zła praktyka kodowania, TRY..CATCHzalecane jest użycie „ ”, ponieważ zostało wprowadzone od SQL Server 2008, a następnie THROWw 2012 roku.

Eddie Kumar
źródło