Como interromper a execução de scripts SQL

16

Estou trabalhando no script sql e estou tendo um requisito de parar de continuar o script se algumas condições não forem atendidas.

Quando o pesquiso no Google, descobri que o RaisError com 20 níveis de gravidade o encerrará. Mas, por algumas razões, não posso usar essa opção.

Pode me fornecer quais são as alternativas possíveis para interromper a execução do script SQL.

Novo desenvolvedor
fonte
11
Por que gerar um erro é inaceitável? Este script também é um procedimento armazenado?
Namphibian
Eu não entendi claramente sua primeira pergunta. Para segunda pergunta; não, este não é um SP
Novo desenvolvedor
11
Qual é o script? Compreende vários lotes? Você já viu as respostas aqui?
Martin Smith

Respostas:

8

Na documentação do RAISERROR (ênfase minha):

Os níveis de gravidade de 0 a 18 podem ser especificados por qualquer usuário. Os níveis de gravidade de 19 a 25 podem ser especificados apenas por membros da função de servidor fixa sysadmin ou usuários com permissões ALTER TRACE. Para níveis de gravidade de 19 a 25, a opção WITH LOG é necessária.

É muito provável que o diretor que você está executando o script não atenda a esses critérios.

Não há nada de errado em usar RAISERROR; você está apenas usando um nível de gravidade excessivo. Eu uso o nível 16 como padrão para um erro gerado e a sequência será encerrada. Se você quiser ser mais preciso, pode seguir os níveis dados pela própria Microsoft:

insira a descrição da imagem aqui

Agora, tendo dito tudo isso, dependendo do contexto do script, o uso RAISERRORpode não ser suficiente, pois não "sai" do script por si só (usando níveis normais de gravidade).

Por exemplo:

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

SELECT 1;   /* Executed! */

Isto irá tanto gerar um erro e retornar um conjunto de resultados.

Para finalizar o script imediatamente, prefiro usar RETURN(o uso de GOTOconstruções -type geralmente é desencorajado na maioria dos círculos de programação em que existem alternativas):

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

SELECT 1;   /* Not executed */

Ou lide com o erro usando TRY/CATCH, o que fará com que a execução pule para o CATCHbloco se a gravidade for 11 ou superior:

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

Um problema separado é se o script abranger vários lotes - RETURNsairá apenas do lote :

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

SELECT 1;   /* Not executed */
GO

SELECT 2;   /* Executed! */

Para corrigir isso, você pode verificar @@ERRORno início de cada lote:

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

SELECT 1;   /* Not executed */
GO

IF (@@ERROR != 0)
    RETURN;

SELECT 2;   /* Not executed */

Editar: Como Martin Smith corretamente aponta nos comentários, isso funciona apenas para 2 lotes. Para estender para 3 ou mais lotes, é possível cascatear os erros de aumento dessa maneira (observação: o GOTOmétodo não resolve esse problema, pois o rótulo de destino deve ser definido no lote):

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

Ou, como ele também aponta, você pode usar o SQLCMDmétodo se for apropriado para o seu ambiente.

Jon Seigel
fonte
Essa última sugestão não funciona. Veja pastebin . Eu gosto do método sqlcmd aqui
Martin Smith
6

Você pode utilizar a GOTOdeclaração para pular para onde quiser. Em outras palavras, você encontra um erro ou alguma outra condição e pode ter um rótulo na parte inferior do script (ou seja TheEndOfTheScript:) e apenas emitir uma goto TheEndOfTheScript;declaração.

Aqui está uma amostra rápida:

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

A saída desta execução será a seguinte:

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

Como você pode ver, o documento GOTOpulou a impressão da terceira e da quarta instruções e pulou direto para a etiqueta ( TheEndOfTheScript).

Thomas Stringer
fonte
7
Só funciona quando existe um único lote, será interrompido assim que você tiver uma instrução GO.
Gabriel
0

Concorde com o SET NOEXEC ON/OFF , no entanto, em Procs armazenados (contendo um único bloco), simplesmente uso a RETURNinstrução

Advertências: em um arquivo de script, se você tiver várias GOinstruções, oRETURN sairá apenas do bloco atual e continuará com o próximo bloco / lote.

Nota: GOTOé uma prática de codificação inadequada, o uso de " TRY..CATCH" é recomendado, pois foi introduzido desde o SQL Server 2008, seguido por THROW2012.

Eddie Kumar
fonte