Jak sprawić, aby sqlcmd zwrócił ERRORLEVEL inny niż 0, gdy skrypt .sql zawiedzie?

38

Korzystam z narzędzia sqlcmd z pliku wsadowego i zastanawiałem się, jak sprawić, by zwrócił błąd ERRORLEVEL inny niż 0, gdy coś pójdzie nie tak z kopią zapasową.

leeand00
źródło
Czy rozważałeś użycie czegoś bardziej niezawodnego (szczególnie przy obsłudze błędów) niż pliki wsadowe?
Aaron Bertrand
Och, masz na myśli Powershell? Właśnie stosowaliśmy zalecaną przez firmę metodę dla produktu, którego używamy. Problem jest zalecaną metodą zakładającą, że tworzysz kopię zapasową jednej bazy danych; ale pokonałem tę przeszkodę. Więc nie ma sposobu, aby rzucić kod błędu, jeśli kopia zapasowa nie działa?
leeand00
3
Za to, co jest warte, w mojej obecnej pracy korzystamy z zapasowych procedur przechowywanych Oli Hallengren (z kilkoma wewnętrznymi procedurami przechowywanymi wokół nich) do tworzenia kopii zapasowych tysięcy baz danych na setkach serwerów i nie mieliśmy z nimi problemów.
James L

Odpowiedzi:

54

Powinieneś użyć opcji -b w sqlcmd.

-b Określa, że ​​sqlcmd kończy działanie i zwraca wartość DOS ERRORLEVEL, gdy wystąpi błąd. Wartość zwracana do zmiennej DOS ERRORLEVEL wynosi 1, gdy komunikat o błędzie programu SQL Server ma poziom ważności większy niż 10; w przeciwnym razie zwracana jest wartość 0

http://msdn.microsoft.com/en-us/library/ms162773.aspx

Ola Hallengren
źródło
10

Ze strony narzędzia MSDN SQLCMD pod adresem : http://msdn.microsoft.com/en-us/library/ms162773.aspx

Jeśli RAISERROR zostanie użyty w skrypcie sqlcmd, a stan 127 zostanie podniesiony, sqlcmd zakończy pracę i zwróci identyfikator komunikatu z powrotem do klienta. Na przykład:

RAISERROR (50001, 10, 127)

Możesz więc zawinąć BACKUP DATABASE...polecenie w TRY...CATCHblok i wywołać odpowiedni komunikat o błędzie, który sqlcmdnastępnie powróci do pliku wsadowego.

Na przykład rozważ następujący errorleveltest.sqlplik:

:ON ERROR EXIT
BEGIN TRY
    /* creates error 3147 Backup and restore operations 
            are not allowed on database tempdb */
    BACKUP DATABASE tempdb; 
END TRY
BEGIN CATCH
    DECLARE @msg NVARCHAR(255);
    SET @msg = 'An error occurred: ' + ERROR_MESSAGE();
    RAISERROR (50002, 10, 127);
END CATCH

I następujący plik .bat: (pierwszy wiersz jest zawijany dla czytelności, ale musi znajdować się w jednym wierszu).

@echo off
"C:\Program Files\Microsoft SQL Server\110\Tools\Binn\sqlcmd" -S "someinstance" -E 
    -i .\errorleveltest.sql
ECHO Errorlevel: %ERRORLEVEL%

Zwraca to następujące polecenie z wiersza polecenia cmd.exe:

Msg 18054, Level 16, State 1, Server CP708-D377\MV, Line 9
Error 50002, severity 10, state 127 was raised, but no message with that error number 
was found in sys.messages. If error is larger than 50000, make sure the user-defined 
message is added using sp_addmessage.
Errorlevel: 1

Jak widać, zwracany jest ERRORLEVEL 1 zamiast normalnej wartości 0. Nie mogę uzyskać ERRORLEVEL odzwierciedlającej faktyczny numer błędu, w tym przypadku 50002; być może jest jakiś problem w moim kodzie, a może jest jakiś problem z moim środowiskiem.

Max Vernon
źródło
1
Przekonałem się, że działa to TYLKO, jeśli podasz wartość liczbową jako pierwszy parametr RAISERROR. na przykład: Zwraca% poziom błędu% = 1: RAISERROR (50002, 10,127) Ale zwraca%% poziom błędu% = 0: RAISERROR ('myErrMsg., 10,127)
5
Informacje dotyczące ostatniego akapitu: ERRORLEVELnie należy oczekiwać, że będą miały tę samą wartość, co numer błędu zgłoszony z SQL Server. ErrorNumber jest wartością specyficzną dla SQL Server, która wskazuje, dlaczego (tj. SQL Server) została zakończona. Z drugiej strony ERRORLEVELjest wartością specyficzną dla SQLCMD, która wskazuje, dlaczego (tj. SQLCMD) została zakończona.
Solomon Rutzky,