J'ai besoin d'effectuer une MISE À JOUR et une INSÉRER en une seule transaction. Ce code fonctionne bien seul, mais j'aimerais pouvoir l'appeler facilement et transmettre les paramètres requis. Lorsque j'essaie d'imbriquer cette transaction dans une procédure stockée, je rencontre de nombreuses erreurs de syntaxe.
Comment puis-je encapsuler le code suivant afin qu'il puisse être facilement appelé?
BEGIN TRANSACTION AssignUserToTicket
GO
DECLARE @updateAuthor varchar(100)
DECLARE @assignedUser varchar(100)
DECLARE @ticketID bigint
SET @updateAuthor = 'user1'
SET @assignedUser = 'user2'
SET @ticketID = 123456
UPDATE tblTicket SET ticketAssignedUserSamAccountName = @assignedUser WHERE (ticketID = @ticketID);
INSERT INTO [dbo].[tblTicketUpdate]
([ticketID]
,[updateDetail]
,[updateDateTime]
,[userSamAccountName]
,[activity])
VALUES
(@ticketID,
'Assigned ticket to ' + @assignedUser,
GetDate(),
@updateAuthor,
'Assign');
GO
COMMIT TRANSACTION AssignUserToTicket
Vous aimez avoir besoin d'encapsuler ce code dans CREATE PROCEDURE ...
syntaxe, et supprimez les instructions GO
après BEGIN TRANSACTION
et avant COMMIT TRANSACTION
.
GO
CREATE PROCEDURE dbo.AssignUserToTicket
(
@updateAuthor varchar(100)
, @assignedUser varchar(100)
, @ticketID bigint
)
AS
BEGIN
BEGIN TRANSACTION;
SAVE TRANSACTION MySavePoint;
SET @updateAuthor = 'user1';
SET @assignedUser = 'user2';
SET @ticketID = 123456;
BEGIN TRY
UPDATE dbo.tblTicket
SET ticketAssignedUserSamAccountName = @assignedUser
WHERE (ticketID = @ticketID);
INSERT INTO [dbo].[tblTicketUpdate]
(
[ticketID]
,[updateDetail]
,[updateDateTime]
,[userSamAccountName]
,[activity]
)
VALUES (
@ticketID
, 'Assigned ticket to ' + @assignedUser
, GetDate()
, @updateAuthor
, 'Assign'
);
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
BEGIN
ROLLBACK TRANSACTION MySavePoint; -- rollback to MySavePoint
END
END CATCH
END;
GO
Notez également que j'ai ajouté un TRY...CATCH
bloc d'instructions pour permettre d'effectuer un ROLLBACK TRANSACTION
instruction en cas d'erreur. Vous avez probablement besoin d'une meilleure gestion des erreurs que cela, mais sans connaître vos besoins, c'est difficile au mieux.
Quelques bonnes lectures:
Si vous souhaitez gérer correctement les procédures stockées imbriquées qui peuvent gérer les transactions (qu'elles soient démarrées à partir de T-SQL ou du code d'application), vous devez suivre le modèle que j'ai décrit dans la réponse suivante:
Sommes-nous tenus de gérer la transaction dans le code C # ainsi que dans la procédure stockée
Vous remarquerez deux différences par rapport à ce que vous tentez ici:
L'utilisation de RAISERROR
dans le bloc CATCH
. Cela fait remonter l'erreur jusqu'au niveau de l'appel (que ce soit dans la couche DB ou app) afin qu'une décision puisse être prise concernant le fait qu'une erreur s'est produite.
Non SAVE TRANSACTION
. Je n'ai jamais trouvé de cas pour l'utiliser. Je sais que certaines personnes le préfèrent, mais dans tout ce que j'ai fait à n'importe quel endroit où j'ai travaillé, la notion d'une erreur se produisant dans l'un des niveaux imbriqués impliquait que tout travail déjà effectué était invalide. En utilisant SAVE TRANSACTION
vous revenez seulement à l'état juste avant l'appel de cette procédure stockée, laissant le processus existant comme autrement valide.
Si vous souhaitez plus de détails sur SAVE TRANSACTION
, alors veuillez consulter les informations de cette réponse:
Comment restaurer lorsque 3 procédures stockées sont démarrées à partir d'une procédure stockée
Un autre problème avec SAVE TRANSACTION
est une nuance de son comportement, comme indiqué dans la page MSDN pour SAVE TRANSACTION (soulignement ajouté):
Les noms de point de sauvegarde en double sont autorisés dans une transaction, mais une instruction ROLLBACK TRANSACTION qui spécifie le nom du point de sauvegarde ne ramènera la transaction que vers la plus récente SAVE TRANSACTION à l'aide de ce nom.
Cela signifie que vous devez être très prudent pour donner à chaque point de sauvegarde de chaque procédure stockée un nom unique sur tous les points de sauvegarde de toutes les procédures stockées. Les exemples suivants illustrent ce point.
Ce premier exemple montre ce qui se passe lorsque vous réutilisez le nom du point d'enregistrement; seul le point de sauvegarde de niveau le plus bas est annulé.
IF (OBJECT_ID(N'tempdb..#SaveTranTestA') IS NOT NULL)
BEGIN
DROP TABLE #SaveTranTestA;
END;
CREATE TABLE #SaveTranTestA (SomeVal INT NOT NULL);
BEGIN TRAN; -- start level 1
SAVE TRANSACTION MySavePoint;
SELECT @@TRANCOUNT AS [TranCount]; -- 1
INSERT INTO #SaveTranTestA (SomeVal) VALUES (100);
BEGIN TRAN; -- start level 2
SAVE TRANSACTION MySavePoint;
SELECT @@TRANCOUNT AS [TranCount]; -- 2
INSERT INTO #SaveTranTestA (SomeVal) VALUES (200);
COMMIT; -- exit level 2
SELECT @@TRANCOUNT AS [TranCount]; -- 1
SELECT * FROM #SaveTranTestA;
-- 100
-- 200
ROLLBACK TRANSACTION MySavePoint; -- error occurred; undo actions up to this point
SELECT @@TRANCOUNT AS [TranCount]; -- 1
SELECT * FROM #SaveTranTestA;
-- 100
COMMIT; -- exit level 1
SELECT @@TRANCOUNT AS [TranCount]; -- 0
SELECT * FROM #SaveTranTestA;
-- 100
Ce deuxième exemple montre ce qui se passe lorsque vous utilisez des noms de point de sauvegarde uniques; le point de sauvegarde du niveau souhaité est annulé.
IF (OBJECT_ID(N'tempdb..#SaveTranTestB') IS NOT NULL)
BEGIN
DROP TABLE #SaveTranTestB;
END;
CREATE TABLE #SaveTranTestB (SomeVal INT NOT NULL);
BEGIN TRAN; -- start level 1
SAVE TRANSACTION MySavePointUno;
SELECT @@TRANCOUNT AS [TranCount]; -- 1
INSERT INTO #SaveTranTestB (SomeVal) VALUES (100);
BEGIN TRAN; -- start level 2
SAVE TRANSACTION MySavePointDos;
SELECT @@TRANCOUNT AS [TranCount]; -- 2
INSERT INTO #SaveTranTestB (SomeVal) VALUES (200);
COMMIT; -- exit level 2
SELECT @@TRANCOUNT AS [TranCount]; -- 1
SELECT * FROM #SaveTranTestB;
-- 100
-- 200
ROLLBACK TRANSACTION MySavePointUno; --error occurred; undo actions up to this point
SELECT @@TRANCOUNT AS [TranCount]; -- 1
SELECT * FROM #SaveTranTestB;
-- <no rows>
COMMIT; -- exit level 1
SELECT @@TRANCOUNT AS [TranCount]; -- 0
SELECT * FROM #SaveTranTestB;
-- <no rows>