単一のトランザクションでUPDATEとINSERTを実行する必要があります。そのコードはそれ自体で問題なく機能しますが、簡単に呼び出して必要なパラメーターを渡すことができるようにしたいと思います。このトランザクションをストアドプロシージャにネストしようとすると、多くの構文エラーが発生します。
次のコードをカプセル化して簡単に呼び出せるようにするにはどうすればよいですか?
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
そのコードをCREATE PROCEDURE ...
構文でラップし、BEGIN TRANSACTION
の後とCOMMIT TRANSACTION
の前のGO
ステートメントを削除する必要があります。
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
また、エラーが発生した場合にTRY...CATCH
ステートメントを実行できるように、ROLLBACK TRANSACTION
ステートメントブロックを追加しました。おそらくそれよりも優れたエラー処理が必要ですが、要件の知識がないと、それは最高の状態で困難です。
いくつかの良い読書:
トランザクションを処理できるネストされたストアドプロシージャを適切に処理する場合(T-SQLから開始したか、アプリコードから開始したかに関係なく)、次の回答で説明したテンプレートに従う必要があります。
C#コードおよびストアドプロシージャでトランザクションを処理する必要があります
ここで試みているものとは2つの違いがあることに気づくでしょう。
RAISERROR
ブロック内でのCATCH
の使用。これにより、エラーが呼び出しレベル(DBまたはアプリレイヤーのいずれか)までバブリングされるため、エラーが発生したという事実に関して決定を下すことができます。
いいえSAVE TRANSACTION
。私はこれを使用するためのケースを見つけたことがありません。私は一部の人々がそれを好むことを知っていますが、私が働いたことのある場所でこれまでに行ったことすべてにおいて、ネストされたレベルのいずれかでエラーが発生するという概念は、すでに行われた作業が無効であることを意味しました。 SAVE TRANSACTION
を使用すると、このストアドプロシージャが呼び出される直前の状態に戻るだけで、既存のプロセスは他の方法で有効なままになります。
SAVE TRANSACTION
の詳細が必要な場合は、この回答の情報をご覧ください。
1つのストアドプロシージャから3つのストアドプロシージャが開始されたときにロールバックする方法
SAVE TRANSACTION
のもう1つの問題は、MSDNページの SAVE TRANSACTION (強調を追加)に記載されているように、その動作のニュアンスです。
トランザクションでは重複するセーブポイント名を使用できますが、セーブポイント名を指定するROLLBACK TRANSACTIONステートメントは、トランザクションを最新SAVE TRANSACTIONにロールバックするだけですその名前。
つまり、各ストアドプロシージャの各セーブポイントに、すべてのストアドプロシージャのすべてのセーブポイントで一意の名前を付けるように注意する必要があります。次の例は、この点を示しています。
この最初の例は、セーブポイント名を再利用するとどうなるかを示しています。最下位レベルのセーブポイントのみがロールバックされます。
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
この2番目の例は、一意のセーブポイント名を使用するとどうなるかを示しています。目的のレベルのセーブポイントがロールバックされます。
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>