SQL Server: detener o interrumpir la ejecución de un script SQL
-
20-08-2019 - |
Pregunta
¿Hay alguna manera de detener inmediatamente la ejecución de un script SQL en el servidor SQL, como un " break " o " salir " comando?
Tengo un script que realiza algunas validaciones y búsquedas antes de que comience a insertar, y quiero que se detenga si alguna de las validaciones o búsquedas falla.
Solución
El método raiserror
raiserror('Oh no a fatal error', 20, -1) with log
Esto terminará la conexión, lo que detendrá la ejecución del resto del script.
Tenga en cuenta que tanto el nivel de gravedad 20 o superior como la opción WITH LOG
son necesarios para que funcione de esta manera.
Esto incluso funciona con declaraciones GO, por ejemplo.
print 'hi'
go
raiserror('Oh no a fatal error', 20, -1) with log
go
print 'ho'
Te dará el resultado:
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.
Observe que 'ho' no se imprime.
CUEVAS:
- Esto solo funciona si ha iniciado sesión como administrador (rol 'sysadmin'), y también lo deja sin conexión a la base de datos.
- Si NO ha iniciado sesión como administrador, la llamada RAISEERROR () fallará y el script continuará ejecutándose .
- Cuando se invoca con sqlcmd.exe, se informará el código de salida 2745.
El método noexec
Otro método que funciona con las declaraciones GO es set noexec on
. Esto hace que se omita el resto del script. No termina la conexión, pero debe desactivar noexec
nuevamente antes de que se ejecute cualquier comando.
Ejemplo:
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.
Otros consejos
Simplemente use un RETURN (funcionará tanto dentro como fuera de un procedimiento almacenado).
Si puede usar el modo SQLCMD, entonces el encantamiento
:on error exit
(INCLUYENDO los dos puntos) hará que RAISERROR detenga realmente el script. Por ejemplo,
: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'
generará:
Msg 50000, Level 15, State 10, Line 3
This is not a Valid Instance Database
** An error was encountered during execution of batch. Exiting.
y el lote se detendrá. Si el modo SQLCMD no está activado, obtendrá un error de análisis sobre los dos puntos. Desafortunadamente, no es completamente a prueba de balas, ya que si el script se ejecuta sin estar en modo SQLCMD, ¡SQL Managment Studio no pasa por alto los errores de tiempo de análisis! Aún así, si los está ejecutando desde la línea de comandos, está bien.
No usaría RAISERROR: SQL tiene sentencias IF que pueden usarse para este propósito. Haga su validación y búsquedas y establezca variables locales, luego use el valor de las variables en las declaraciones IF para hacer que las inserciones sean condicionales.
No necesitaría verificar un resultado variable de cada prueba de validación. Por lo general, puede hacer esto con solo una variable de indicador para confirmar que se han cumplido todas las condiciones:
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
Incluso si su validación es más compleja, solo debería necesitar algunas variables de marca para incluir en su (s) verificación (es) final (es).
Extendí la solución de activación / desactivación de noexec con éxito con una transacción para ejecutar el script de manera todo o nada.
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
Aparentemente, el compilador " entiende " la variable @finished en el IF, incluso si hubo un error y la ejecución fue deshabilitada. Sin embargo, el valor se establece en 1 solo si la ejecución no se deshabilitó. Por lo tanto, puedo confirmar o deshacer la transacción en consecuencia.
puede ajustar su declaración SQL en un ciclo WHILE y usar BREAK si es necesario
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
En SQL 2012+, puede usar THROW .
THROW 51000, 'Stopping execution because validation failed.', 0;
PRINT 'Still Executing'; -- This doesn't execute with THROW
Desde MSDN:
Genera una excepción y transfiere la ejecución a un bloque CATCH de un TRY & # 8230; construcción CATCH ... Si un TRY & # 8230; construcción CATCH no está disponible, la sesión finaliza. Se establece el número de línea y el procedimiento donde se genera la excepción. La gravedad se establece en 16.
¿Es este un procedimiento almacenado? Si es así, creo que podría hacer un retorno, como & Quot; Return NULL & Quot ;;
Refinando aún más el método Sglasses, las líneas anteriores fuerzan el uso del modo SQLCMD, y treminan el scirpt si no están usando el modo SQLCMD o usan :on error exit
para salir ante cualquier error
CONTEXT_INFO se utiliza para realizar un seguimiento del estado.
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-------------
Sugeriría que envuelva su bloque de código apropiado en un bloque try catch. Luego puede usar el evento Raiserror con una gravedad de 11 para ir al bloque de captura si lo desea. Si solo desea aumentar los horrores pero continuar la ejecución dentro del bloque de prueba, use una gravedad menor.
¿Tiene sentido?
Saludos, John
[Editado para incluir referencia BOL]
http://msdn.microsoft.com/en -us / library / ms175976 (SQL.90) .aspx
Puede alterar el flujo de ejecución utilizando declaraciones GOTO :
IF @ValidationResult = 0
BEGIN
PRINT 'Validation fault.'
GOTO EndScript
END
/* our code */
EndScript:
puede usar RAISERROR .
Ninguno de estos funciona con declaraciones 'GO'. En este código, independientemente de si la gravedad es 10 u 11, obtienes la declaración PRINT final.
Script de prueba:
-- =================================
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
Resultados :
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 única forma de hacer que esto funcione es escribir el script sin GO
declaraciones. A veces eso es fácil. A veces es bastante difícil. (Use algo como IF @error <> 0 BEGIN ...
.)
Esta fue mi solución:
...
BEGIN
raiserror('Invalid database', 15, 10)
rollback transaction
return
END
Puede usar la declaración GOTO. Prueba esto. Este es el uso completo para usted.
WHILE(@N <= @Count)
BEGIN
GOTO FinalStateMent;
END
FinalStatement:
Select @CoumnName from TableName
Yo uso RETURN
aquí todo el tiempo, funciona en script o Stored Procedure
Asegúrese de ROLLBACK
la transacción si está en una, de lo contrario <=> inmediatamente dará como resultado una transacción abierta no confirmada
¡Gracias por la respuesta!
raiserror()
funciona bien, pero no debe olvidar la declaración return
de lo contrario, el script continúa sin error. (¡hense que el raiserror no es un & "; throwerror &" ;-)) y, por supuesto, ¡retroceder si es necesario!
<=> es bueno decirle a la persona que ejecuta el script que algo salió mal.
Si simplemente está ejecutando una secuencia de comandos en Management Studio y desea detener la ejecución o la operación de reversión (si se usa) en el primer error, la mejor manera que considero es usar el bloque try catch (SQL 2005 en adelante). Esto funciona bien en Management Studio si está ejecutando un archivo de script. El proceso almacenado siempre puede usar esto también.
En el pasado utilizamos lo siguiente ... funcionó mejor:
RAISERROR ('Error! Connection dead', 20, 127) WITH LOG