J'essaie d'écrire un script MS SQL qui comporte une transaction et un bloc try/catch. S'il intercepte une exception, la transaction est annulée. Sinon, la transaction est validée. J'ai vu plusieurs sites Web différents disant de le faire comme ceci:
begin transaction
begin try
--main content of script here
end try
begin catch
rollback transaction
end catch
commit transaction
Mais ne frapperons-nous toujours pas la ligne "commit transaction" même si une exception est interceptée? Cela ne mènera-t-il pas à une erreur SQL car la transaction a déjà été annulée? Je pense que cela devrait être fait comme ça:
declare @success bit = 1
begin transaction
begin try
--main content of script here
end try
begin catch
rollback transaction
set @success = 0
end catch
if(@success = 1)
begin
commit transaction
end
Comment la solution couramment publiée n'inclut-elle pas la variable @success? N'y a-t-il pas d'erreur SQL résultant d'une transaction déjà annulée? Ai-je tort de dire que la ligne "commit transaction" du premier exemple de code sera toujours touchée dans le cas de la capture d'une exception?
J'ai toujours pensé c'était l'un des meilleurs articles sur le sujet. Il inclut l'exemple suivant qui, à mon avis, le clarifie et inclut le @@ trancount, souvent oublié, qui est nécessaire pour des transactions imbriquées fiables.
PRINT 'BEFORE TRY'
BEGIN TRY
BEGIN TRAN
PRINT 'First Statement in the TRY block'
INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(1, 'Account1', 10000)
UPDATE dbo.Account SET Balance = Balance + CAST('TEN THOUSAND' AS MONEY) WHERE AccountId = 1
INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(2, 'Account2', 20000)
PRINT 'Last Statement in the TRY block'
COMMIT TRAN
END TRY
BEGIN CATCH
PRINT 'In CATCH Block'
IF(@@TRANCOUNT > 0)
ROLLBACK TRAN;
THROW; -- raise error to the client
END CATCH
PRINT 'After END CATCH'
SELECT * FROM dbo.Account WITH(NOLOCK)
GO
Dans votre premier exemple, vous avez raison. Le lot touchera la transaction de validation, que le bloc try soit déclenché ou non.
Dans votre deuxième exemple, je suis d’accord avec les autres commentateurs. L'utilisation de l'indicateur de réussite est inutile.
Je considère que l'approche suivante est essentiellement une approche allégée des meilleures pratiques.
Si vous voulez voir comment il gère une exception, changez la valeur de la deuxième insertion de 255 à 256.
CREATE TABLE #TEMP ( ID TINYINT NOT NULL );
INSERT INTO #TEMP( ID ) VALUES ( 1 )
BEGIN TRY
BEGIN TRANSACTION
INSERT INTO #TEMP( ID ) VALUES ( 2 )
INSERT INTO #TEMP( ID ) VALUES ( 255 )
COMMIT TRANSACTION
END TRY
BEGIN CATCH
DECLARE
@ErrorMessage NVARCHAR(4000),
@ErrorSeverity INT,
@ErrorState INT;
SELECT
@ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE();
RAISERROR (
@ErrorMessage,
@ErrorSeverity,
@ErrorState
);
ROLLBACK TRANSACTION
END CATCH
SET NOCOUNT ON
SELECT ID
FROM #TEMP
DROP TABLE #TEMP
J'ai utilisé ci-dessous plusieurs fois le modèle de script ms sql avec succès, qui utilise Try-Catch , Commit Transaction- Rollback Transaction , Suivi des erreurs .
Votre bloc TRY sera comme suit
BEGIN TRY
BEGIN TRANSACTION T
----
//your script block
----
COMMIT TRANSACTION T
END TRY
Votre bloc CATCH sera comme suit
BEGIN CATCH
DECLARE @ErrMsg NVarChar(4000),
@ErrNum Int,
@ErrSeverity Int,
@ErrState Int,
@ErrLine Int,
@ErrProc NVarChar(200)
SELECT @ErrNum = Error_Number(),
@ErrSeverity = Error_Severity(),
@ErrState = Error_State(),
@ErrLine = Error_Line(),
@ErrProc = IsNull(Error_Procedure(), '-')
SET @ErrMsg = N'ErrLine: ' + rtrim(@ErrLine) + ', proc: ' + RTRIM(@ErrProc) + ',
Message: '+ Error_Message()
Votre script ROLLBACK fera partie du bloc CATCH comme suit
IF (@@TRANCOUNT) > 0
BEGIN
PRINT 'ROLLBACK: ' + SUBSTRING(@ErrMsg,1,4000)
ROLLBACK TRANSACTION T
END
ELSE
BEGIN
PRINT SUBSTRING(@ErrMsg,1,4000);
END
END CATCH
Au-dessus de différents blocs de script, vous devez utiliser un seul bloc. Si une erreur survient dans le blocTRY, le blocCATCHest utilisé. Il y définit divers détails sur le numéro d'erreur, la gravité de l'erreur, la ligne d'erreur ..etc. Enfin, tous ces détails seront ajoutés au paramètre @ErrMsg. Ensuite, il va vérifier le nombre de transactions (@@ TRANCOUNT> 0), c'est-à-dire s'il y a quelque chose dans la transaction à restaurer. Si c'est le cas, affichez le message d'erreur et ROLLBACK TRANSACTION . Sinon, imprimez simplement le message d'erreur.
Nous avons conservé notre script COMMIT TRANSACTION T vers la dernière ligne du bloc TRY afin de nous assurer qu'il doit valider la transaction (modification finale de la base de données) uniquement après que tout le code du bloc TRY a été exécuté avec succès.
Compteur de transaction
--@@TRANCOUNT = 0
begin try
--@@TRANCOUNT = 0
BEGIN TRANSACTION tran1
--@@TRANCOUNT = 1
--your code
-- if failed @@TRANCOUNT = 1
-- if success @@TRANCOUNT = 0
COMMIT TRANSACTION tran1
end try
begin catch
print 'FAILED'
end catch
Ci-dessous pourrait être utile.
Source: https://msdn.Microsoft.com/en-us/library/ms175976.aspx
BEGIN TRANSACTION;
BEGIN TRY
-- your code --
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber
,ERROR_SEVERITY() AS ErrorSeverity
,ERROR_STATE() AS ErrorState
,ERROR_PROCEDURE() AS ErrorProcedure
,ERROR_LINE() AS ErrorLine
,ERROR_MESSAGE() AS ErrorMessage;
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION;
END CATCH;
IF @@TRANCOUNT > 0
COMMIT TRANSACTION;
GO