Tengo un procedimiento almacenado que se llama en un bloque insert-exec:
insert into @t
exec('test')
¿Cómo puedo manejar las excepciones generadas en el procedimiento almacenado y seguir procesando?
El siguiente código ilustra el problema. Lo que quiero hacer es devolver 0 o -1 dependiendo del éxito o el fracaso de la exec()
llamada interna :
alter procedure test -- or create
as
begin try
declare @retval int;
-- This code assumes that PrintMax exists already so this generates an error
exec('create procedure PrintMax as begin print ''hello world'' end;')
set @retval = 0;
select @retval;
return(@retval);
end try
begin catch
-- if @@TRANCOUNT > 0 commit;
print ERROR_MESSAGE();
set @retval = -1;
select @retval;
return(@retval);
end catch;
go
declare @t table (i int);
insert into @t
exec('test');
select *
from @t;
Mi problema es el return(-1)
. El camino del éxito está bien.
Si omito el bloque try / catch en el procedimiento almacenado, se genera el error y falla la inserción. Sin embargo, lo que quiero hacer es manejar el error y devolver un buen valor.
El código como está devuelve el mensaje:
Msg 3930, Level 16, State 1, Line 6
The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction.
Este es quizás el peor mensaje de error que he encontrado. Parece que realmente significa "No manejó un error en una transacción anidada".
Si pongo el if @@TRANCOUNT > 0
, entonces recibo el mensaje:
Msg 3916, Level 16, State 0, Procedure gordontest, Line 7
Cannot use the COMMIT statement within an INSERT-EXEC statement unless BEGIN TRANSACTION is used first.
He intentado jugar con las declaraciones de transacción de inicio / confirmación, pero nada parece funcionar.
Entonces, ¿cómo puedo hacer que mi procedimiento almacenado maneje errores sin abortar la transacción general?
Editar en respuesta a Martin:
El código de llamada real es:
declare @RetvalTable table (retval int);
set @retval = -1;
insert into @RetvalTable
exec('
declarar @retval int; exec @retval = '+ @ query +'; seleccione @retval ');
select @retval = retval from @RetvalTable;
¿Dónde @query
está la llamada al procedimiento almacenado? El objetivo es obtener el valor de retorno del procedimiento almacenado. Si esto es posible sin un insert
(o, más específicamente, sin iniciar una transacción), sería genial.
No puedo modificar los procedimientos almacenados en general para almacenar el valor en una tabla, porque hay demasiados. Uno de ellos está fallando, y puedo modificar eso. Mi mejor solución actual es algo como:
if (@StoredProcedure = 'sp_rep__post') -- causing me a problem
begin
exec @retval = sp_rep__post;
end;
else
begin
-- the code I'm using now
end;
fuente
declare @t table (i int);declare @RC int;exec @RC = test;insert into @t values (@RC);select * from @t;
funciona bien.select @retval; return @retval
al final. Si conoce otra forma de obtener el valor de retorno de una llamada de procedimiento almacenado dinámico, me encantaría saberlo.DECLARE @RC INT;EXEC sp_executesql N'EXEC @RC = test', N'@RC INT OUTPUT', @RC = @RC OUTPUT;insert into @t VALUES (@RC)
Respuestas:
El error en la
EXEC
parte de laINSERT-EXEC
declaración está dejando su transacción en un estado condenado.Si
PRINT
saleXACT_STATE()
en elCATCH
bloque, está configurado en-1
.No todos los errores establecerán el estado en esto. El siguiente error de restricción de verificación pasa al bloque catch y se realiza
INSERT
correctamente.Agregando esto al
CATCH
bloqueNo ayuda Da el error
No creo que haya una forma de recuperarse de tal error una vez que ha sucedido. Sin
INSERT ... EXEC
embargo, para su caso de uso específico, no necesita de todos modos. Puede asignar el valor de retorno a una variable escalar y luego insertarlo en una declaración separada.O, por supuesto, podría reestructurar el procedimiento almacenado llamado para que no genere ese error en absoluto.
fuente