Dans un bloc de code SQL Server, quel est le meilleur endroit pour placer la transaction de validation? A l'intérieur du bloc try catch ou à l'extérieur?.
Par exemple, l'option A ou l'option B est-elle la bonne approche ou s'agit-il de choix subjectifs?
Option A
CREATE PROCEDURE DummyProc
BEGIN TRY
BEGIN TRANSACTION
INSERT sometable(a, b) VALUES (@a, @b)
INSERT sometable(a, b) VALUES (@b, @a)
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF @@trancount > 0 ROLLBACK TRANSACTION
DECLARE @msg nvarchar(2048) = error_message()
RAISERROR (@msg, 16, 1)
RETURN 55555
END CATCH
Option B
CREATE PROCEDURE DummyProc
BEGIN TRY
BEGIN TRANSACTION
INSERT sometable(a, b) VALUES (@a, @b)
INSERT sometable(a, b) VALUES (@b, @a)
END TRY
BEGIN CATCH
IF @@trancount > 0 ROLLBACK TRANSACTION
DECLARE @msg nvarchar(2048) = error_message()
RAISERROR (@msg, 16, 1)
RETURN 55555
END CATCH
IF @@trancount > 0 COMMIT TRANSACTION
Dans l'option B, existe-t-il une possibilité qu'une erreur se produise lors de sa validation en dehors de TRY-CATCH
bloquer ?
L'option A est le bon choix. Il est possible que toutes les instructions d'une transaction fonctionnent, puis le COMMIT réel échoue, vous gardez donc le COMMIT dans votre bloc TRY afin que toute défaillance du COMMIT soit interceptée et que vous puissiez gérer avec élégance cette erreur et cette restauration.
Voir cette réponse sur SE. Dans votre exemple de code, si nous supposons que d'autres utilisateurs exécutent cette procédure simultanément, il se peut que le bloc TRY se termine avec succès, puis échoue sur COMMIT en raison des modifications apportées par d'autres sessions.
Le meilleur moyen que j'ai trouvé pour le faire est le code suivant:
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRANSACTION
/*
Code goes here
*/
COMMIT TRANSACTION
END TRY
BEGIN CATCH
DECLARE @ErrorMessage NVARCHAR(4000);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;
SELECT
@ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE();
RAISERROR (@ErrorMessage, -- Message text.
@ErrorSeverity, -- Severity.
@ErrorState -- State.
);
-- If >= SQL 2012 replace all code in catch block above with
-- THROW;
WHILE @@TRANCOUNT > 0
BEGIN
ROLLBACK TRANSACTION;
END
END CATCH
Notez l'utilisation de XACT_ABORT pour garantir que les erreurs sont détectées efficacement, le fait que les instructions BEGIN et COMMIT se trouvent dans le bloc TRY et WHILE pour @@ Trancount - cela devrait garantir que les transactions imbriquées sont annulées (pas toujours applicables)
L'instruction THROW peut également remplacer RAISERROR pour les versions SQL supérieures à 2012, pour relancer l'exception/l'erreur interceptée.
Comme Dan Guzman l'indique dans son commentaire, XACT_ABORT est utile pour détecter les erreurs que la construction TRY/CATCH ne fera pas, y compris les délais d'expiration, les erreurs de classement d'exécution, etc.