web-dev-qa-db-fra.com

SQL Server - arrête ou arrête l'exécution d'un script SQL

Existe-t-il un moyen d'arrêter immédiatement l'exécution d'un script SQL sur le serveur SQL, comme une commande "break" ou "exit"?

J'ai un script qui effectue des validations et des recherches avant de commencer à insérer des insertions, et je souhaite qu'il s'arrête si l'une des validations ou recherches échoue.

280
Andy White

Le raiserror method

raiserror('Oh no a fatal error', 20, -1) with log

Cela mettra fin à la connexion, empêchant ainsi l'exécution du reste du script.

Notez que le niveau de gravité 20 ou supérieur et l’option WITH LOG sont nécessaires pour que cela fonctionne de cette façon.

Cela fonctionne même avec les instructions GO, par exemple.

print 'hi'
go
raiserror('Oh no a fatal error', 20, -1) with log
go
print 'ho'

Vous donnera la sortie:

hi
Msg 2745, Level 16, State 2, Line 1
Process ID 51 has raised user error 50000, severity 20. SQL Server is terminating this process.
Msg 50000, Level 20, State 1, Line 1
Oh no a fatal error
Msg 0, Level 20, State 0, Line 0
A severe error occurred on the current command.  The results, if any, should be discarded.

Notez que 'ho' n'est pas imprimé.

CAVEATS:

  • Cela ne fonctionne que si vous êtes connecté en tant qu'administrateur (rôle 'sysadmin') et vous laisse également sans connexion à la base de données.
  • Si vous n'êtes PAS connecté en tant qu'administrateur, l'appel de RAISEERROR () échouera lui-même et le script continuera de s'exécuter.
  • Lorsqu'il est appelé avec sqlcmd.exe, le code de sortie 2745 sera signalé.

Référence: http://www.mydatabasesupport.com/forums/ms-sqlserver/174037-sql-server-2000-abort-whole-script.html#post761334

La méthode noexec

Une autre méthode qui fonctionne avec les instructions GO est set noexec on. Cela provoque le saut du reste du script. Il ne met pas fin à la connexion, mais vous devez désactiver noexec avant de pouvoir exécuter une commande.

Exemple:

print 'hi'
go

print 'Fatal error, script will not continue!'
set noexec on

print 'ho'
go

-- last line of the script
set noexec off -- Turn execution back on; only needed in SSMS, so as to be able 
               -- to run this script again in the same session.
330
Blorgbeard

Utilisez simplement un RETURN (cela fonctionnera à la fois à l'intérieur et à l'extérieur d'une procédure stockée).

163
Gordon Bell

Si vous pouvez utiliser le mode SQLCMD, alors l’incantation 

:on error exit

(Y compris les deux points), RAISERROR arrêtera réellement le script. Par exemple.,

:on error exit

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[SOMETABLE]') AND type in (N'U')) 
    RaisError ('This is not a Valid Instance Database', 15, 10)
GO

print 'Keep Working'

affichera:

Msg 50000, Level 15, State 10, Line 3
This is not a Valid Instance Database
** An error was encountered during execution of batch. Exiting.

et le lot va s'arrêter. Si le mode SQLCMD n'est pas activé, vous obtiendrez une erreur d'analyse concernant les deux points. Malheureusement, comme si le script était exécuté sans être en mode SQLCMD, SQL Managment Studio passe au-delà des erreurs de temps d'analyse! Néanmoins, si vous les exécutez à partir de la ligne de commande, tout va bien.

45
Sglasses

Je n'utiliserais pas RAISERROR- SQL comporte des instructions IF pouvant être utilisées à cette fin. Faites votre validation et vos recherches et définissez des variables locales, puis utilisez la valeur des variables dans les instructions IF pour rendre les insertions conditionnelles.

Vous n'avez pas besoin de vérifier un résultat variable de chaque test de validation. Vous pouvez généralement le faire avec une seule variable indicateur pour confirmer que toutes les conditions sont remplies:

declare @valid bit

set @valid = 1

if -- Condition(s)
begin
  print 'Condition(s) failed.'
  set @valid = 0
end

-- Additional validation with similar structure

-- Final check that validation passed
if @valid = 1
begin
  print 'Validation succeeded.'

  -- Do work
end

Même si votre validation est plus complexe, vous n’auriez besoin que de quelques variables indicatrices à inclure dans vos vérifications finales.

20
Dave Swersky

Dans SQL 2012+, vous pouvez utiliser THROW .

THROW 51000, 'Stopping execution because validation failed.', 0;
PRINT 'Still Executing'; -- This doesn't execute with THROW

De MSDN:

Lève une exception et transfère l'exécution à un bloc CATCH d'une construction TRY… CATCH ... Si une construction TRY… CATCH n'est pas disponible, la session est terminée. Le numéro de ligne et la procédure où l'exception est levée sont définis. La gravité est définie sur 16.

12
Jordan Parker

J'ai étendu la solution noexec on/off avec succès avec une transaction permettant d'exécuter le script de manière tout ou rien. 

set noexec off

begin transaction
go

<First batch, do something here>
go
if @@error != 0 set noexec on;

<Second batch, do something here>
go
if @@error != 0 set noexec on;

<... etc>

declare @finished bit;
set @finished = 1;

SET noexec off;

IF @finished = 1
BEGIN
    PRINT 'Committing changes'
    COMMIT TRANSACTION
END
ELSE
BEGIN
    PRINT 'Errors occured. Rolling back changes'
    ROLLBACK TRANSACTION
END

Apparemment, le compilateur "comprend" la variable @finished dans le SI, même s'il y avait une erreur et que l'exécution était désactivée. Cependant, la valeur est définie sur 1 uniquement si l'exécution n'a pas été désactivée. Par conséquent, je peux bien valider ou annuler la transaction en conséquence.

12
Tz_

vous pouvez envelopper votre instruction SQL dans une boucle WHILE et utiliser BREAK si nécessaire

WHILE 1 = 1
BEGIN
   -- Do work here
   -- If you need to stop execution then use a BREAK


    BREAK; --Make sure to have this break at the end to prevent infinite loop
END
12
Jon Erickson

En plus de la méthode Sglasses de raffinement, les lignes ci-dessus forcent l’utilisation du mode SQLCMD, et transfèrent le scirpt si le mode SQLCMD n’est pas utilisé ou utilisent :on error exit pour sortir de toute erreur
CONTEXT_INFO est utilisé pour suivre l’état. 

SET CONTEXT_INFO  0x1 --Just to make sure everything's ok
GO 
--treminate the script on any error. (Requires SQLCMD mode)
:on error exit 
--If not in SQLCMD mode the above line will generate an error, so the next line won't hit
SET CONTEXT_INFO 0x2
GO
--make sure to use SQLCMD mode ( :on error needs that)
IF CONTEXT_INFO()<>0x2 
BEGIN
    SELECT CONTEXT_INFO()
    SELECT 'This script must be run in SQLCMD mode! (To enable it go to (Management Studio) Query->SQLCMD mode)\nPlease abort the script!'
    RAISERROR('This script must be run in SQLCMD mode! (To enable it go to (Management Studio) Query->SQLCMD mode)\nPlease abort the script!',16,1) WITH NOWAIT 
    WAITFOR DELAY '02:00'; --wait for the user to read the message, and terminate the script manually
END
GO

----------------------------------------------------------------------------------
----THE ACTUAL SCRIPT BEGINS HERE-------------
8
jaraics

Est-ce une procédure stockée? Si c'est le cas, je pense que vous pouvez simplement faire un retour, tel que "Return NULL";

7
mtazva

Je suggérerais que vous encapsuliez votre bloc de code approprié dans un bloc catch catch. Vous pouvez ensuite utiliser l'événement Raiserror avec une sévérité de 11 afin de passer au bloc catch si vous le souhaitez. Si vous souhaitez simplement expliquer des erreurs mais poursuivre l'exécution dans le bloc try, utilisez une gravité inférieure.

Avoir un sens?

A bientôt, John

[Édité pour inclure BOL Reference]

http://msdn.Microsoft.com/en-us/library/ms175976(SQL.90).aspx

6
John Sansom

Vous pouvez modifier le flux d'exécution à l'aide de GOTO statement:

IF @ValidationResult = 0
BEGIN
    PRINT 'Validation fault.'
    GOTO EndScript
END

/* our code */

EndScript:
6
Charlie

Aucun de ces travaux avec les déclarations «GO». Dans ce code, que la gravité soit 10 ou 11, vous obtenez l'instruction PRINT finale.

Script de test:

-- =================================
PRINT 'Start Test 1 - RAISERROR'

IF 1 = 1 BEGIN
    RAISERROR('Error 1, level 11', 11, 1)
    RETURN
END

IF 1 = 1 BEGIN
    RAISERROR('Error 2, level 11', 11, 1)
    RETURN
END
GO

PRINT 'Test 1 - After GO'
GO

-- =================================
PRINT 'Start Test 2 - Try/Catch'

BEGIN TRY
    SELECT (1 / 0) AS CauseError
END TRY
BEGIN CATCH
    SELECT ERROR_MESSAGE() AS ErrorMessage
    RAISERROR('Error in TRY, level 11', 11, 1)
    RETURN
END CATCH
GO

PRINT 'Test 2 - After GO'
GO

Résultats:

Start Test 1 - RAISERROR
Msg 50000, Level 11, State 1, Line 5
Error 1, level 11
Test 1 - After GO
Start Test 2 - Try/Catch
 CauseError
-----------

ErrorMessage
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Divide by zero error encountered.

Msg 50000, Level 11, State 1, Line 10
Error in TRY, level 11
Test 2 - After GO

La seule façon de faire ce travail est d'écrire le script sans les instructions GO. Parfois c'est facile. Parfois c'est assez difficile. (Utilisez quelque chose comme IF @error <> 0 BEGIN ....)

4
Rob Garrison

vous pouvez utiliser RAISERROR .

4
Mladen Prajdic

C'était ma solution:

...

BEGIN
    raiserror('Invalid database', 15, 10)
    rollback transaction
    return
END
3

Vous pouvez utiliser l'instruction GOTO. Essaye ça. C'est une utilisation complète pour vous.

WHILE(@N <= @Count)
BEGIN
    GOTO FinalStateMent;
END

FinalStatement:
     Select @CoumnName from TableName
3
Vishal Kiri

J'utilise RETURN ici tout le temps, fonctionne en script ou Stored Procedure

Assurez-vous que vous avez ROLLBACK la transaction si vous en avez une, sinon RETURN entraînera immédiatement une transaction ouverte non validée.

2
jerryhung

Merci pour la réponse!

raiserror() fonctionne bien, mais vous ne devez pas oublier la déclaration return sinon le script continue sans erreur! (car le raiserror n'est pas un "throwerror" ;-)) et bien sûr faire un retour en arrière si nécessaire!

raiserror() est agréable à dire à la personne qui exécute le script que quelque chose s'est mal passé.

1
hfrmobile

Si vous exécutez simplement un script dans Management Studio et souhaitez arrêter l'exécution ou la transaction d'annulation (si elle est utilisée) à la première erreur, le meilleur moyen, à mon avis, est d'utiliser try catch block (SQL 2005 et ultérieur) . bien en studio de gestion si vous exécutez un fichier de script . proc stocké peut toujours l'utiliser aussi.

1
Bhargav Shah

De retour dans la journée, nous utilisions les éléments suivants ... qui fonctionnaient le mieux:

RAISERROR ('Error! Connection dead', 20, 127) WITH LOG
0
Lee