Como fazer com que o sqlcmd retorne um ERRORLEVEL diferente de 0 quando o script .sql falha?

38

Estou executando o sqlcmd a partir de um arquivo em lotes e queria saber como fazê-lo retornar um ERRORLEVEL diferente de 0 quando algo der errado com o backup.

leeand00
fonte
Você já pensou em usar algo mais robusto (principalmente no tratamento de erros) do que arquivos em lote?
Aaron Bertrand
Oh, você quer dizer como Powershell? Estávamos seguindo o método recomendado pela empresa para o produto que estamos usando. O problema é que o método recomendado assume que você está fazendo backup de um banco de dados; mas eu superei esse obstáculo. Portanto, não há como fazê-lo lançar apenas um código de erro se o backup não funcionar?
precisa saber é
3
Pelo que vale a pena, no meu trabalho atual, usamos os procedimentos armazenados de backup de Ola Hallengren (com alguns procedimentos armazenados internos do wrapper) para fazer backup de milhares de bancos de dados em centenas de servidores, e não tivemos problemas com eles.
James L

Respostas:

54

Você deve usar a opção -b no sqlcmd.

-b Especifica que o sqlcmd sai e retorna um valor DOS ERRORLEVEL quando ocorre um erro. O valor retornado para a variável DOS ERRORLEVEL é 1 quando a mensagem de erro do SQL Server tem um nível de gravidade maior que 10; caso contrário, o valor retornado é 0

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

Ola Hallengren
fonte
10

Na página Utilitário MSDN SQLCMD em: http://msdn.microsoft.com/en-us/library/ms162773.aspx

Se RAISERROR for usado em um script sqlcmd e um estado 127 for aumentado, o sqlcmd será encerrado e retornará o ID da mensagem ao cliente. Por exemplo:

RAISERROR (50001, 10, 127)

Portanto, você pode agrupar o BACKUP DATABASE...comando em um TRY...CATCHbloco e gerar a mensagem de erro apropriada, que sqlcmdretornaria ao seu arquivo em lotes.

Por exemplo, considere o seguinte errorleveltest.sqlarquivo:

: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

E o seguinte arquivo .bat: (a primeira linha está ajustada para facilitar a leitura, mas precisa estar em uma única linha.)

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

Isso retorna o seguinte do meu prompt do 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

Como você pode ver, ERRORLEVEL 1 é retornado em vez do valor normal de retorno 0. Não consigo fazer com que ERRORLEVEL reflita o número de erro real, neste caso 50002; talvez haja um problema no meu código ou talvez haja algum problema no meu ambiente.

Max Vernon
fonte
1
Descobri que isso SOMENTE funciona se você fornecer um valor numérico como o primeiro parâmetro de RAISERROR. por exemplo: Isso retorna um% errorlevel% = 1: RAISERROR (50002, 10.127) Mas isso retorna% errorlevel% = 0: RAISERROR ('myErrMsg.', 10.127)
5
Para sua informação a respeito do último parágrafo: ERRORLEVELnão se deve esperar o mesmo valor que o número do erro relatado no SQL Server. O ErrorNumber é um valor específico do SQL Server para indicar por que ele (ou seja, SQL Server) foi encerrado. Por outro lado, ERRORLEVELé um valor específico do SQLCMD para indicar por que ele (ou seja, SQLCMD) terminou.
Solomon Rutzky