Как заставить sqlcmd возвращать значение ERRORLEVEL, отличное от 0, в случае сбоя сценария .sql?

38

Я запускаю sqlcmd из командного файла, и мне было интересно, как заставить его вернуть ERRORLEVEL, отличный от 0, когда что-то не так с резервной копией.

leeand00
источник
Рассматривали ли вы использовать что-то более надежное (особенно при обработке ошибок), чем командные файлы?
Аарон Бертран
О, ты имеешь в виду как Powershell? Мы только что выбрали рекомендованный компанией метод для продукта, который мы используем. Проблема в том, что рекомендуемый метод предполагает резервное копирование одной базы данных; но я преодолел это препятствие. Так что нет способа заставить его просто выбросить код ошибки, если резервная копия не работает?
leeand00
3
Для моей текущей работы мы используем хранимые процедуры резервного копирования Ola Hallengren (с некоторыми внутренними хранимыми процедурами-оболочками) для резервного копирования тысяч баз данных на сотнях серверов, и у нас с ними не было проблем.
Джеймс Л

Ответы:

54

Вы должны использовать опцию -b в sqlcmd.

-b Указывает, что sqlcmd завершает работу и возвращает значение DOS ERRORLEVEL при возникновении ошибки. Значение, которое возвращается в переменную DOS ERRORLEVEL, равно 1, если сообщение об ошибке SQL Server имеет уровень серьезности больше 10; в противном случае возвращается 0

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

Ола Хелленгрен
источник
10

Со страницы служебной программы MSDN SQLCMD по адресу: http://msdn.microsoft.com/en-us/library/ms162773.aspx.

Если RAISERROR используется в скрипте sqlcmd и состояние 127 поднято, sqlcmd завершит работу и вернет идентификатор сообщения обратно клиенту. Например:

RAISERROR (50001, 10, 127)

Таким образом, вы можете заключить BACKUP DATABASE...команду в TRY...CATCHблок и вызвать соответствующее сообщение об ошибке, которое sqlcmdзатем вернется в ваш командный файл.

Например, рассмотрим следующий errorleveltest.sqlфайл:

: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

И следующий файл .bat: (первая строка переносится для удобства чтения, но должна быть в одной строке.)

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

Это возвращает следующее из моей подсказки 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

Как вы можете видеть, ERRORLEVEL 1 возвращается вместо обычного возвращаемого значения 0. Я не могу получить ERRORLEVEL, чтобы отразить фактический номер ошибки, в данном случае 50002; возможно, в моем коде есть проблема, или, возможно, есть какая-то проблема с моей средой.

Макс Вернон
источник
1
Я обнаружил, что это работает ТОЛЬКО если вы указали числовое значение в качестве первого параметра RAISERROR. Например: возвращает% errorlevel% = 1: RAISERROR (50002, 10,127), но возвращает% errorlevel% = 0: RAISERROR ('myErrMsg.', 10,127)
5
К вашему сведению, что касается последнего абзаца: ERRORLEVELне следует ожидать, чтобы оно совпадало с номером ошибки, сообщенным с SQL Server. ErrorNumber - это специфическое для SQL Server значение, указывающее, почему оно (т. Е. SQL Server) завершено. С другой стороны, ERRORLEVELэто специфичное для SQLCMD значение, указывающее, почему оно (то есть SQLCMD) завершено.
Соломон Руцкий