저장 프로 시저에서의 트랜잭션


12

단일 트랜잭션에서 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

1
정확히 "오류"가 무엇인지에 대한 세부 정보를 질문에 추가하면 도움이 될 것입니다 ( 질문 아래 의 편집 링크 사용 ). 또한 둘러보세요 . 감사!
Max Vernon

답변:


15

해당 코드를 CREATE PROCEDURE ...구문 으로 감싸고 그 GO전후 의 명령문을 제거 BEGIN TRANSACTION해야 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

또한 오류가 발생할 경우 명령문을 TRY...CATCH수행 할 수 있도록 명령문 블록을 추가했습니다 ROLLBACK TRANSACTION. 아마도 그보다 더 나은 오류 처리가 필요하지만 요구 사항에 대한 지식이 없으면 최선의 방법이 아닙니다.

좋은 독서 :

  1. 항상 스키마를 지정하십시오

  2. 저장 프로 시저 모범 사례

  3. 피해야 할 나쁜 습관


1
여전히 저장된 거래를 원합니다. SP에 트랜잭션을 넣고 SP가 다른 트랜잭션에 래핑되면 물건이 실패합니다. sqlstudies.com/2014/01/06/…
Kenneth Fisher

저를 지적 해 주셔서 감사합니다. SQL Server에 중첩 트랜잭션이 없다는 것을 알고 있지만 SAVE TRANS명령의 의미를 알지 못했습니다 .
Max Vernon

8

트랜잭션을 처리 할 수있는 중첩 된 저장 프로 시저를 올바르게 처리하려면 (T-SQL 또는 앱 코드에서 시작했는지 여부) 다음 답변에서 설명한 템플릿을 따라야합니다.

C # 코드 및 저장 프로 시저에서 트랜잭션을 처리해야합니까?

여기서 시도하는 것과는 두 가지 차이점이 있습니다.

  1. 블록 RAISERROR내에서 의 사용 CATCH. 이렇게하면 (DB 또는 앱 계층에 관계없이) 호출 수준까지 오류가 발생하므로 오류가 발생한 사실에 대한 결정을 내릴 수 있습니다.

  2. 없음 SAVE TRANSACTION. 나는 이것을 사용하는 경우를 찾지 못했습니다. 나는 어떤 사람들이 그것을 선호한다는 것을 알고 있지만, 내가 일한 곳에서 내가 한 모든 일에서 중첩 된 수준 내에서 발생하는 오류의 개념은 이미 수행 된 모든 작업이 잘못되었다는 것을 암시했다. 를 사용 SAVE TRANSACTION하면이 스토어드 프로 시저가 호출되기 직전의 상태로 되돌아 가기 때문에 기존 프로세스는 유효하지 않습니다.

    에 대한 자세한 내용 SAVE TRANSACTION을 보려면이 답변의 정보를 살펴보십시오.

    하나의 저장 프로 시저에서 3 개의 저장 프로 시저가 시작될 때 롤백하는 방법

    SAVE TRANSACTION (강조 추가) SAVE TRANSACTION에 대한 MSDN 페이지에서 언급했듯이 또 다른 문제 는 동작의 미묘한 차이입니다 .

    트랜잭션에서 중복 저장 점 이름이 허용되지만 저장 점 이름을 지정하는 ROLLBACK TRANSACTION 문은 해당 이름을 사용하여 트랜잭션을 가장 최근의 SAVE TRANSACTION으로 롤백합니다 .

    즉, 각 저장 프로 시저의 각 저장 지점에 모든 저장 프로 시저의 모든 저장 지점에서 고유 한 이름을 지정해야합니다. 다음 예제는이 점을 보여줍니다.

    이 첫 번째 예는 Save Point 이름을 재사용 할 때 발생하는 상황을 보여줍니다. 가장 낮은 수준의 저장 지점 만 롤백됩니다.

    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
    

    이 두 번째 예는 고유 한 저장 점 이름을 사용할 때 발생하는 상황을 보여줍니다. 원하는 레벨의 저장 점이 롤백됩니다.

    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>
    

그래서 @@ NESTLEVEL을 사용하여 SAVE TRANSACTION 세이브 포인트 이름을 제작합니다.
Vincent Vancalbergh
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.