2010-04-26 14 views
6

Tengo un script sql que se establece para pasar a producción. Envolví los diversos proyectos en transacciones separadas. En cada una de las transacciones creamos procedimientos almacenados. Recibo mensajes de errorTSQL - crear un proceso almacenado dentro de una declaración de transacción

Msg 156, nivel 15, estado 1, línea 4 Sintaxis incorrecta cerca de la palabra clave 'procedure'.

he creado este script de ejemplo para ilustrar

Begin Try 
Begin Transaction 
    -- do a bunch of add/alter tables here 
    -- do a bunch of data manipulation/population here 

    -- create a stored proc 
    create procedure dbo.test 
    as 
    begin 
    select * from some_table 
    end 
Commit 
End Try 
Begin Catch 
    Rollback 
    Declare @Msg nvarchar(max) 
    Select @Msg=Error_Message(); 
    RaisError('Error Occured: %s', 20, 101,@Msg) With Log; 
End Catch 

El error parece dar a entender que no puedo crear procedimientos almacenados dentro de la transacción, pero no estoy encontrando ninguna documentación que dicen lo contrario (tal vez google ISN siendo amable hoy).

+0

es esto para el servidor sql? –

+0

sí, lo siento, debería haber mencionado eso. sql 2008. –

+0

Decidí sacar el proceso de creación de la transacción y probar/atraparlo en su propia sección. Puse una declaración "if exists then drop" y agregué una (s) declaración (es) "if exists then print message" (existente) alrededor de la creación del proceso almacenado. Gracias por todos los comentarios, aprendí algo de eso. –

Respuesta

10

hacer el create procedure en EXEC('...'), como este intento:

Begin Try 
Begin Transaction 
    -- do a bunch of add/alter tables here 
    -- do a bunch of data manipulation/population here 

    -- create a stored proc 
    EXEC ('create procedure dbo.test 
    as 
    begin 
    select * from some_table 
    end') 
Commit 
End Try 
Begin Catch 
    Rollback 
    Declare @Msg nvarchar(max) 
    Select @Msg=Error_Message(); 
    RaisError('Error Occured: %s', 20, 101,@Msg) With Log; 
End Catch 

GO 
+3

+1. Esto tiene que hacerse, ya que la llamada 'create procedure ...' tiene que ser la primera instrucción en el script. Ponerlo en su propio lote con 'EXEC' resuelve esto. Esto también se puede usar para crear archivos de script que crean o actualizan un procedimiento almacenado. Muy útil para el archivo de script de procedimiento. –

+0

En la muestra, si lo ejecuta directamente (ponga un nombre de tabla real para some_table) con las cosas ddl/dml comentadas, el error aún ocurre, y sin embargo, el procedimiento de creación es la primera declaración, o no entiendo bien. –

+0

el código funciona bien para mí, no estoy seguro de lo que está cambiando –

0

Me parece recordar que no puede hacer cosas como crear, modificar o dejar esquema de base de datos de objetos — incluyendo procedimientos almacenados — dentro de una transacción (ya que tales estructural los cambios no son transaccionales: no puede deshacerlos con cambios de datos).

+1

He probado el código de mi respuesta, no solo crea un procedimiento almacenado, pero si fuerza un ROLLBACK en el código, también lo retrotraerá. –

+1

@KM: Esto podría haber cambiado en versiones más recientes de SQL Server, por supuesto. – Richard

10

No puede escribir su script de esta manera ya que hay muchas instrucciones que deben ejecutarse en su propio lote. En su lugar, yo recomendaría hacer algo parecido a SQL de cómo Red-Puerta de comparación construye sus guiones:

Set Xact_Abort On 
GO 
Begin Transaction 
GO 
If object_id('tempdb..#tmpErrors') is not null 
    Drop Table #tmpErrors 
GO 
Create Table #tmpErrors([Error] int not null) 
GO 
Create Procedure dbo.Test 
As 
Begin 
    --.... 
End 
GO 
If @@Error <> 0 AND @@TranCount >0 Rollback Transaction 
GO 
IF @@TranCount = 0 Begin Insert #tmpErrors (Error) Select 1 Begin Transaction End 
GO 

-- more statements 

GO 
If @@Error <> 0 AND @@TranCount >0 Rollback Transaction 
GO 
IF @@TranCount = 0 Begin Insert #tmpErrors (Error) Select 1 Begin Transaction End 
GO 

--..... 

IF NOT EXISTS(SELECT * FROM #tmpErrors) 
    BEGIN 
     PRINT 'The database update succeeded' 
     IF @@TRANCOUNT > 0 COMMIT TRANSACTION 
    END 
ELSE 
    BEGIN 
     PRINT 'The database update failed' 
     IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION 
    END 
GO 

DROP TABLE #tmpErrors 
GO 
+0

SSMS se queja de que 'Error' no es una columna en' #tmpErrors (Error) Seleccione 1' si es 'ID' (o' Create Table #tmpErrors (Id int not null) 'debe ser' Create Table #tmpErrors (Error int not null) '? –

+0

@SamHolder - Sí. Eso es un error tipográfico de mi parte que corregí. Si vas con' Id', entonces la instrucción Create Table obviamente debería usar 'Id'. Si vas con' Error', entonces la declaración Create Table necesita ser actualizada para ese hecho. – Thomas

+0

Gracias por aclarar. +1 de mi parte. Gran respuesta –

2

Hay varios problemas con la solución propuesta por KM:

  • Si el contenido de lo que poner su llamada EXEC() es semánticamente incorrecta (por ejemplo, coloca una tabla inexistente en FROM dentro del procedimiento almacenado), ese error no se dispara y la transacción no se revierte.

  • Si el contenido de lo que coloca en su llamada EXEC() es incorrecto sintácticamente (por ejemplo, coloca SELECTT en lugar de SELECT dentro del procedimiento almacenado), la transacción parece retrotraer pero aparece un error completamente críptico :

    A transport-level error has occurred when receiving results from the server. (provider: TCP Provider, error: 0 - The specified network name is no longer available.) 
    

Así que estoy todavía en una pérdida de cómo crear un procedimiento dentro de una transacción, pero todavía tienen la transacción y el try-catch ser útiles.

+0

Utilice el bloque CATCH que está en la respuesta de Mile aquí: http://stackoverflow.com/questions/180075/executing-a-stored-procedure-inside -begin-end-transaction – knocte

Cuestiones relacionadas