2012-03-15 3 views
12

특정 상황에서 모든 작업을 취소하고 호출자에게 오류 코드를 반환 할 수 있도록 저장 지점을 설정해야하는 저장 프로 시저가 있습니다. , 수락/커밋하고 호출자에게 성공을 반환합니다. 그러나 호출자가 이미 트랜잭션을 시작했는지 여부를 확인해야합니다. 의사는이 주제에 대해 매우 혼란 스럽습니다. 여기 내가 생각하는대로 작동하지만, 모든 파급 효과가 확실하지 않습니다.SAVE TRANSACTION 대 BEGIN TRANSACTION (SQL Server) 트랜잭션을 멋지게 중첩하는 방법

것은 -이 Stored Procedure (SP)은 다른 사람이 호출합니다. 따라서 거래를 시작했는지 여부는 알 수 없습니다 ... 사용자가 내 SP를 사용하기 위해 거래를 시작하도록 요구하더라도 여전히 Save Points의 올바른 사용에 대한 질문이 있습니다 ...

내 SP는 거래가 진행 중인지 여부를 테스트하고 그렇지 않은 경우 BEGIN TRANSACTION으로 시작합니다. 트랜잭션이 이미 진행 중이면 대신 SAVE TRANSACTION MySavePointName으로 저장 지점을 만들고이 사실을 저장합니다.

그런 다음 변경 사항을 롤백해야한다면 BEGIN TRANSACTION을 먼저 수행하면 ROLLBACK TRANSACTION이됩니다. 세이브 포인트를했다면 ROLLBACK TRANSACTION MySavePointName입니다. 이 시나리오는 훌륭하게 작동하는 것 같습니다.

여기 내가 다소 혼란스러워하는 곳입니다. 제가 한 일을 계속하고 싶다면, 제가 거래를 시작하면 COMMIT TRANSACTION을 실행할 것입니다. 하지만 저장 지점을 만들었습니까? 나는 COMMIT TRANSACTION MySavePointName을 시도했지만 다음 호출자는 트랜잭션을 커밋을 시도하고 오류 가져옵니다 그래서

The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.

그때 궁금하네요 - 세이브 포인트 롤백 할 수 있습니다 (작업 : ROLLBACK TRANSACTION MySavePointName가 호출자의 롤백 할 것이다 트랜잭션). 하지만 아마도 "커밋"할 필요가 없을 것입니다. 원래 트랜잭션이 커밋 (또는 롤백)되면 다시 롤백 할 필요가 없기 때문에 그대로 유지됩니다.

거래를 "중첩"하는 "더 좋은"방법이있는 경우 빛을 비추십시오. BEGIN TRANSACTION으로 중첩하는 방법을 알아 내지 못했지만 내부 트랜잭션 만 롤백하거나 커밋합니다. ROLLBACK은 항상 상단 트랜잭션으로 롤백되며, COMMIT은 단순히 @@trancount만큼 감소합니다.

+0

귀하의 발견은 답변으로 게시 가치가있을 수도 있습니다. –

+0

@Andriy Ok, 그게 내가 한 짓이야. 내 편집을 제거하고 대신 대답으로 사용 했어. 감사. –

답변

19

나는 지금 밖으로 모두이 문제를 냈어요 생각, 그래서 당신은 그래서 http://geekswithblogs.net/bbiales/archive/2012/03/15/how-to-nest-transactions-nicely---quotbegin-transactionquot-vs-quotsave.aspx

내 SP에서 더 자세한 정보를 원한다면 나는 심지어 내 연구 결과를 블로그에 한 ...

을 내 자신의 질문에 대답합니다 이런 일에, 아무도가없는 경우 새 트랜잭션을 시작하지만 하나가 이미 진행중인 경우 저장 포인트를 사용하기 시작 : 준비 변경 사항을 적용 할 때

DECLARE @startingTranCount int 
SET @startingTranCount = @@TRANCOUNT 

IF @startingTranCount > 0 
    SAVE TRANSACTION mySavePointName 
ELSE 
    BEGIN TRANSACTION 
-- … 

그런 다음, 당신이 경우에만 커밋 할 필요가 우리는 거래를 스스로 시작했습니다 :

IF @startingTranCount = 0 
    COMMIT TRANSACTION 

그리고 마지막으로, 당신의 변경 지금까지 롤백 :

-- Roll back changes... 
IF @startingTranCount > 0 
    ROLLBACK TRANSACTION MySavePointName 
ELSE 
    ROLLBACK TRANSACTION 
+0

'save transaction' 후에 새로운 중첩 트랜잭션을 시작하지 않는 이유는 무엇입니까? – sotn

+0

다음은 중첩 트랜잭션에 대한 기사에 대한 링크입니다. 중첩 된 트랜잭션이 커밋되면 중첩 된 트랜잭션 수를 낮추지 만 아무 것도 커밋하지 않습니다. 따라서 외부 트랜잭션이 커밋되면 모든 것이 롤백되고 "커밋 된"트랜잭션을 포함하여 모든 롤백이 수행됩니다. 중첩 된 트랜잭션이 롤백을 수행하면 자체 트랜잭션과 외부 트랜잭션을 롤백합니다. 이미있는 트랜잭션에 영향을 미치지 않고 자신의 트랜잭션 만 롤백하고 싶다면 여기에 설명 된 기술을 사용하십시오. –

+0

다음은 중첩 트랜잭션에 대한 기사입니다. https://technet.microsoft.com/en-us/library/ms189336%28v=sql.105%29.aspx?f=255&MSPPError=-2147217396 –

10

Brian B's answer 확장.

이렇게하면 저장 지점 이름이 고유하고 SQL Server 2012의 새로운 TRY/CATCH/THROW 기능을 사용합니다.

DECLARE @mark CHAR(32) = replace(newid(), '-', ''); 
DECLARE @trans INT = @@TRANCOUNT; 

IF @trans = 0 
    BEGIN TRANSACTION @mark; 
ELSE 
    SAVE TRANSACTION @mark; 

BEGIN TRY 
    -- do work here 

    IF @trans = 0 
     COMMIT TRANSACTION @mark; 
END TRY 
BEGIN CATCH 
    IF xact_state() = 1 OR (@trans = 0 AND xact_state() <> 0) ROLLBACK TRANSACTION @mark; 
    THROW; 
END CATCH 
+0

다시 방문하지 않았습니다. 이 게시물에 잠시 동안,하지만 당신은 다음 쿼리에서 축 어적으로 재사용 할 수있는 템플릿을 좋아합니다. 좋은 향상. –

+0

try 블록 안에 트랜잭션 문을 시작하는 MSDN 제안을 보았습니다. 이 패턴으로 그렇게하면 몇 가지 문제가 발생하거나 그렇게하는 것이 안전할까요? –

+0

catch 블록의 if 문에 대해 혼란스러워합니다. xact state = -1이면 트랜잭션을 롤백하는 이유는 무엇입니까? 그것을 <> - 1일까요? –

2

내 저장 프로 시저에서 트랜잭션 관리자의이 유형을 사용하고 있습니다 :

CREATE PROCEDURE Ardi_Sample_Test 
     @InputCandidateID INT 
    AS 
     DECLARE @TranCounter INT; 
     SET @TranCounter = @@TRANCOUNT; 
     IF @TranCounter > 0 
      SAVE TRANSACTION ProcedureSave; 
     ELSE 
      BEGIN TRANSACTION; 
     BEGIN TRY 

      /* 
      <Your Code> 
      */ 

      IF @TranCounter = 0 
       COMMIT TRANSACTION; 
     END TRY 
     BEGIN CATCH 
      IF @TranCounter = 0 
       ROLLBACK TRANSACTION; 
      ELSE 
       IF XACT_STATE() <> -1 
        ROLLBACK TRANSACTION ProcedureSave; 

      DECLARE @ErrorMessage NVARCHAR(4000); 
      DECLARE @ErrorSeverity INT; 
      DECLARE @ErrorState INT; 
      SELECT @ErrorMessage = ERROR_MESSAGE(); 
      SELECT @ErrorSeverity = ERROR_SEVERITY(); 
      SELECT @ErrorState = ERROR_STATE(); 

      RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState); 
     END CATCH 
    GO 
관련 문제