J'ai une procédure stockée qui est appelée dans un bloc insert-exec:
insert into @t
exec('test')
Comment gérer les exceptions générées dans la procédure stockée et continuer le traitement?
Le code suivant illustre le problème. Ce que je veux faire, c'est retourner 0 ou -1 selon le succès ou l'échec de l' exec()
appel interne :
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;
Mon problème est le return(-1)
. Le chemin du succès est bien.
Si je laisse de côté le bloc try / catch dans la procédure stockée, l'erreur est déclenchée et l'insertion échoue. Cependant, ce que je veux faire, c'est gérer l'erreur et renvoyer une belle valeur.
Le code tel quel renvoie le message:
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.
C'est peut-être le pire message d'erreur que j'ai rencontré. Cela semble vraiment signifier «Vous n'avez pas géré d'erreur dans une transaction imbriquée».
Si je mets le if @@TRANCOUNT > 0
, je reçois le message:
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.
J'ai essayé de jouer avec les instructions de transaction begin / commit, mais rien ne semble fonctionner.
Alors, comment puis-je faire en sorte que ma procédure stockée gère les erreurs sans abandonner la transaction globale?
Modifier en réponse à Martin:
Le code d'appel réel est:
declare @RetvalTable table (retval int);
set @retval = -1;
insert into @RetvalTable
exec('
declare @retval int; exec @retval = '+ @ query +'; sélectionnez @retval ');
select @retval = retval from @RetvalTable;
Où se @query
trouve l'appel de procédure stockée. Le but est d'obtenir la valeur de retour de la procédure stockée. Si cela est possible sans insert
(ou, plus précisément, sans démarrer de transaction), ce serait formidable.
Je ne peux pas modifier les procédures stockées en général pour stocker la valeur dans une table, car il y en a trop. L'un d'eux échoue et je peux le modifier. Ma meilleure solution actuelle est quelque chose comme:
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;
la source
declare @t table (i int);declare @RC int;exec @RC = test;insert into @t values (@RC);select * from @t;
fonctionne bien.select @retval; return @retval
à la fin. Si vous connaissez une autre façon d'obtenir la valeur de retour d'un appel de procédure stockée dynamique, j'aimerais savoir.DECLARE @RC INT;EXEC sp_executesql N'EXEC @RC = test', N'@RC INT OUTPUT', @RC = @RC OUTPUT;insert into @t VALUES (@RC)
Réponses:
L'erreur dans la
EXEC
partie de laINSERT-EXEC
déclaration laisse votre transaction dans un état condamné.Si vous
PRINT
sortezXACT_STATE()
dans leCATCH
bloc, il est réglé sur-1
.Toutes les erreurs ne définissent pas l'état sur ceci. L'erreur de contrainte de vérification suivante passe par le bloc catch et
INSERT
réussit.Ajout de cela au
CATCH
blocN'aide pas. Il donne l'erreur
Je ne pense pas qu'il y ait moyen de se remettre d'une telle erreur une fois qu'elle s'est produite. Pour votre cas d'utilisation spécifique, vous n'avez cependant pas besoin de
INSERT ... EXEC
toute façon. Vous pouvez affecter la valeur de retour à une variable scalaire, puis l'insérer dans une instruction distincte.Ou bien sûr, vous pouvez restructurer la procédure stockée appelée afin qu'elle ne déclenche pas du tout cette erreur.
la source