EXECUTE 이후 트랜잭션 수는 BEGIN 및 COMMIT 문의 일치하지 않는 수를 나타냅니다. 이전 카운트 = 1, 현재 카운트 = 0


95

나는이 Insert데이터를 공급합니다 저장 프로 시저 Table1와 수 Column1의 값을 Table1과 표 2를 공급합니다 번째 저장 프로 시저를 호출합니다.

하지만 두 번째 저장 프로 시저를 다음과 같이 호출하면

Exec USPStoredProcName

다음과 같은 오류가 발생합니다.

EXECUTE 이후 트랜잭션 수는 BEGIN 및 COMMIT 문의 일치하지 않는 수를 나타냅니다. 이전 카운트 = 1, 현재 카운트 = 0.

나는 다른 질문에 대한 답변을 읽었으며 정확히 커밋 수가 엉망이되는 곳을 찾을 수 없습니다.


절차에 TRY / CATCH 블록이 있습니까?
Remus Rusanu 2014

그래, 난 TRY / CATCH 블록이
인 Vignesh 쿠마

답변:


110

TRY / CATCH 블록이있는 경우 가능한 원인은 트랜잭션 중단 예외를 포착하고 계속하는 것입니다. CATCH 블록에서는 항상 XACT_STATE()적절한 중단 및 커밋 할 수없는 (두꺼운) 트랜잭션을 확인 하고 처리 해야 합니다. 호출자가 트랜잭션을 시작하고 calee가 교착 상태 (트랜잭션을 중단 함)에 도달하면 호출 수신자가 트랜잭션이 중단되었으며 '평상시처럼 비즈니스'를 계속해서는 안된다는 것을 호출자에게 어떻게 전달할 것인가? 가능한 유일한 방법은 예외를 다시 발생시켜 호출자가 상황을 처리하도록하는 것입니다. 중단 된 트랜잭션을 조용히 삼키고 호출자가 계속해서 원래 트랜잭션에 있다고 가정하면 신체 상해 만이 보장 할 수 있습니다 (그리고 발생하는 오류는 엔진이 자체 보호를 시도하는 방식입니다).

중첩 트랜잭션 및 예외와 함께 사용할 수있는 패턴을 보여주는 예외 처리 및 중첩 트랜잭션살펴볼 것을 권장합니다 .

create procedure [usp_my_procedure_name]
as
begin
    set nocount on;
    declare @trancount int;
    set @trancount = @@trancount;
    begin try
        if @trancount = 0
            begin transaction
        else
            save transaction usp_my_procedure_name;

        -- Do the actual work here

lbexit:
        if @trancount = 0
            commit;
    end try
    begin catch
        declare @error int, @message varchar(4000), @xstate int;
        select @error = ERROR_NUMBER(), @message = ERROR_MESSAGE(), @xstate = XACT_STATE();
        if @xstate = -1
            rollback;
        if @xstate = 1 and @trancount = 0
            rollback
        if @xstate = 1 and @trancount > 0
            rollback transaction usp_my_procedure_name;

        raiserror ('usp_my_procedure_name: %d: %s', 16, 1, @error, @message) ;
    end catch
end
go

3
당신의 도움을 주셔서 감사합니다. Raiserror를 사용하여 문제를 발견했습니다. NOT NULL 필드에 NULL 값을 삽입하려고 시도하는 것입니다
Vignesh Kumar A

그러나 제약 조건 확인 유효성 검사는 트랜잭션을 중단하지 않습니다. 캐치에서 명시 적으로 롤백하고 xact_abort on있습니까?
Remus Rusanu 2014

Im 명시 적으로 롤백
Vignesh Kumar A 2014

2
이 패턴을 시도했지만 여전히 작동하지 않습니다. 외부 트랜잭션이있을 때이 패턴이 저장 점을 생성하고 심각한 오류 (불완전한 트랜잭션)의 경우 외부 트랜잭션을 롤백합니다.이 패턴은 여전히 ​​입력하기 전에 @@ trancount = 1을 발생시킵니다. 절차 및 @@ TRANCOUNT = 0을 종료 할 때
참새

3
CATCH의이 비트가 잘못되었다고 생각합니다. MSDN 예제를if @xstate = -1 rollback; 보면 외부 트랜잭션 이 없는 경우 (즉, 그렇지 않은 경우) 전체 트랜잭션을 롤백 해서는 안됩니다 . 절차는 트랜잭션을 시작 해야만 @sparrow의 문제를 해결할 수 있다고 생각합니다 . begin tranrollback
Nick

62

나도이 문제가 있었다. 저에게 이유는 제가하고 있었기 때문입니다

return
commit

대신에

commit
return   

하나의 저장 프로 시저에서.


4
@seguso-이것은 매우 도움이되었습니다. 공유 해주셔서 감사합니다. 때로는 무언가가 단순히 먼지 아래에 있습니다. 최선을 다해 일어납니다.
레오 Gurdian

이것은 나에게 문제 였지만 데이터 액세스 레이어를 통해 하나의 큰 트랜잭션에서 여러 sproc 호출을 래핑했기 때문에 명확하지 않았습니다. 이 문제가있는 경우 트랜잭션을 생성하는 sproc 외부에 무언가가 없는지 확인하십시오. 그렇다면 sproc 내에서 return 문을 전혀 사용하지 못할 수도 있습니다.
EF0

이것은 나 였고 트랜잭션이 있었고 if / else 문에서 커밋 트랜잭션 전에 반환되었습니다
Kevin

19

이는 일반적으로 트랜잭션이 시작되고 커밋되지 않았거나 롤백되지 않았을 때 발생합니다.

저장 프로 시저에 오류가 발생하는 경우 예외 처리없이 일부 런타임 오류로 인해 트랜잭션이 완료되지 않아 데이터베이스 테이블을 잠글 수 있습니다. 아래와 같이 예외 처리를 사용할 수 있습니다. SET XACT_ABORT

SET XACT_ABORT ON
SET NoCount ON
Begin Try 
     BEGIN TRANSACTION 
        //Insert ,update queries    
     COMMIT
End Try 
Begin Catch 
     ROLLBACK
End Catch

출처


이 경우 인용 된 질문 / 답변은이 질문이 중복 된 것으로 표시되고 종료되어야 함을 의미해야합니다.
Mark Schultheiss

10

중첩 된 트랜잭션을 사용하는 경우 ROLLBACK 작업은 가장 바깥쪽에있는 트랜잭션을 포함하여 모든 중첩 된 트랜잭션을 롤백합니다.

TRY / CATCH와 함께 사용하면 설명 된 오류가 발생할 수 있습니다. 여기에서 더 많은 것을 보십시오 .


5

저장 프로 시저가 트랜잭션을 연 후 컴파일 실패 (예 : 테이블을 찾을 수 없음, 잘못된 열 이름)가 발생하는 경우에도 발생할 수 있습니다.

Remus Rusanu가 설명한 것과 비슷한 논리를 사용하여 "worker"하나와 try / catch가있는 래퍼 하나를 2 개의 저장 프로 시저를 사용해야한다는 것을 알았습니다. 작업자 캐치는 "정상적인"실패를 처리하는 데 사용되며 래퍼 캐치는 컴파일 실패 오류를 처리하는 데 사용됩니다.

https://msdn.microsoft.com/en-us/library/ms175976.aspx

TRY… CATCH 구문의 영향을받지 않는 오류

다음 유형의 오류는 TRY… CATCH 구문과 동일한 실행 수준에서 발생할 때 CATCH 블록에서 처리되지 않습니다 .

  • 구문 오류와 같은 컴파일 오류배치 실행을 방해하는 .
  • 지연된 이름 확인으로 인해 컴파일 후 발생하는 개체 이름 확인 오류와 같이 문 수준 재 컴파일 중에 발생하는 오류입니다.

다른 사람이 디버깅 시간을 절약하는 데 도움이되기를 바랍니다.


1
고마워 저스틴. 좋은 관찰입니다. 내 경우에는 내가 참으로 잘못된 구문을 저장 SP 동안 컴파일 오류가 발생하지만했다하지 않는 업데이 트 내부 집계를하고 있었다 - "집계는 UPDATE 문의 SET 목록에 나타나지 않을 수 있습니다"
kuklei

4

내 경우에는 오류가 발생되고 있던 RETURN내부 BEGIN TRANSACTION. 그래서 나는 다음과 같은 것을 가졌습니다.

Begin Transaction
 If (@something = 'foo')
 Begin
     --- do some stuff
     Return
 End
commit

다음과 같아야합니다.

Begin Transaction
 If (@something = 'foo')
 Begin
     --- do some stuff
     Rollback Transaction ----- THIS WAS MISSING
     Return
 End
commit

2

광범위한 디버깅 후 저에게 수정은 간단한 누락이었습니다. 롤백 후 catch의 문. 그것 없이는이 추악한 오류 메시지가 당신이 끝낼 것입니다.

begin catch
    if @@trancount > 0 rollback transaction;
    throw; --allows capture of useful info when an exception happens within the transaction
end catch

2

동일한 오류 메시지가 있었는데 COMMIT TRANSACTION 줄 끝에 세미콜론이 있다는 실수가있었습니다.


이렇게 간단합니다. 또한 SP가 완전히 실행되지 않는 경우 'ROLLBACK'문이 필요했습니다. 거래를 종료 / 종료하기 위해.
J Cordero

1

내 거래 에서이 진술을 생략 한 후이 오류가 한 번 발생했습니다.

COMMIT TRANSACTION [MyTransactionName]

1

제 생각에 받아 들여진 대답은 대부분의 경우 과잉입니다.

오류의 원인은 오류에 의해 명확하게 언급 된 BEGIN과 COMMIT의 불일치입니다. 이것은 다음을 사용하는 것을 의미합니다.

Begin
  Begin
    -- your query here
  End
commit

대신에

Begin Transaction
  Begin
    -- your query here
  End
commit

시작 후 트랜잭션을 생략하면이 오류가 발생합니다!


1

동일한 프로 시저 / 쿼리에 하나 이상의 트랜잭션이 커밋되지 않은 상태로 남아 있지 않은지 확인하십시오.

제 경우에는 실수로 쿼리에 BEGIN TRAN 문이있었습니다.


1

이는 C # 코드에서 SP를 호출하는 방식에 따라 달라질 수도 있습니다. SP가 일부 테이블 유형 값을 반환하면 ExecuteStoreQuery를 사용하여 SP를 호출하고 SP가 값을 반환하지 않으면 ExecuteStoreCommand를 사용하여 SP를 호출합니다.


1

사용하지 마십시오

RETURN

당신이 사용할 때 진술

BEGIN TRY
    ... 
END TRY

BEGIN CATCH
    ...
END CATCH

BEGIN, COMMIT & ROLLBACK

SQL 저장 프로 시저의 문


0

다음과 같은 코드 구조가있는 경우 :

SELECT 151
RETURN -151

그런 다음 다음을 사용하십시오.

SELECT 151
ROLLBACK
RETURN -151
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.