XACT_ABORT가 ON으로 설정된 경우 CATCH 블록 내부에서 트랜잭션을 커밋 할 수있는 경우는 무엇입니까?


13

TRY...CATCH와 에 대한 MSDN을 읽었습니다 XACT_STATE.

트랜잭션의 커밋 또는 롤백 여부를 결정하기 위해 구성 블록 XACT_STATE에서 사용하는 다음 예제가 있습니다.CATCHTRY…CATCH

USE AdventureWorks2012;
GO

-- SET XACT_ABORT ON will render the transaction uncommittable
-- when the constraint violation occurs.
SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRANSACTION;
        -- A FOREIGN KEY constraint exists on this table. This 
        -- statement will generate a constraint violation error.
        DELETE FROM Production.Product
            WHERE ProductID = 980;

    -- If the delete operation succeeds, commit the transaction. The CATCH
    -- block will not execute.
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    -- Test XACT_STATE for 0, 1, or -1.
    -- If 1, the transaction is committable.
    -- If -1, the transaction is uncommittable and should 
    --     be rolled back.
    -- XACT_STATE = 0 means there is no transaction and
    --     a commit or rollback operation would generate an error.

    -- Test whether the transaction is uncommittable.
    IF (XACT_STATE()) = -1
    BEGIN
        PRINT 'The transaction is in an uncommittable state.' +
              ' Rolling back transaction.'
        ROLLBACK TRANSACTION;
    END;

    -- Test whether the transaction is active and valid.
    IF (XACT_STATE()) = 1
    BEGIN
        PRINT 'The transaction is committable.' + 
              ' Committing transaction.'
        COMMIT TRANSACTION;   
    END;
END CATCH;
GO

내가 이해하지 못하는 것은 왜 어떤 XACT_STATE수익을 보살 피고 점검해야 하는가?

이 예 에서는 플래그 XACT_ABORT가로 설정되어 ON있습니다.

TRY블록 내부에 심각한 오류가있는 경우 제어는로 전달됩니다 CATCH. 따라서 내부에있는 경우 CATCH거래에 문제가 있음을 알고 실제로이 경우 할 수있는 유일한 일은 롤백하는 것입니까?

그러나 MSDN 의이 예제는 제어가 전달 될 때 CATCH트랜잭션이 커밋되는 경우가 있음을 의미합니다. 누군가가 일어날 수있을 때, 이해가 될 때 실제적인 예를 제시 할 수 있습니까?

로 설정되어 있을 때CATCH 커밋 XACT_ABORTON 수있는 트랜잭션으로 컨트롤을 전달할 수있는 경우는 없습니다 .

에 대한 MSDN 기사 SET XACT_ABORT에는 트랜잭션 내부의 일부 문이 성공적으로 실행되고 일부 XACT_ABORT가로 설정되어 있으면 실패하는 예가 OFF있습니다. 그러나 블록 내부에서 1 SET XACT_ABORT ONXACT_STATE()반환 하는 방법은 CATCH무엇입니까?

처음에는 다음과 같이이 코드를 작성했을 것입니다.

USE AdventureWorks2012;
GO

-- SET XACT_ABORT ON will render the transaction uncommittable
-- when the constraint violation occurs.
SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRANSACTION;
        -- A FOREIGN KEY constraint exists on this table. This 
        -- statement will generate a constraint violation error.
        DELETE FROM Production.Product
            WHERE ProductID = 980;

    -- If the delete operation succeeds, commit the transaction. The CATCH
    -- block will not execute.
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    -- Some severe problem with the transaction
    PRINT 'Rolling back transaction.';
    ROLLBACK TRANSACTION;
END CATCH;
GO

Max Vernon의 답변을 고려하면 다음과 같은 코드를 작성합니다. 그는 시도하기 전에 활성 트랜잭션이 있는지 확인하는 것이 합리적임을 보여주었습니다 ROLLBACK. 그러나 함께 블록 중 하나 전혀 트랜잭션이나 트랜잭션 (transaction)를 운명 수 있습니다. 따라서 어쨌든 아무것도 없습니다 . 내가 잘못?SET XACT_ABORT ONCATCHCOMMIT

USE AdventureWorks2012;
GO

-- SET XACT_ABORT ON will render the transaction uncommittable
-- when the constraint violation occurs.
SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRANSACTION;
        -- A FOREIGN KEY constraint exists on this table. This 
        -- statement will generate a constraint violation error.
        DELETE FROM Production.Product
            WHERE ProductID = 980;

    -- If the delete operation succeeds, commit the transaction. The CATCH
    -- block will not execute.
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    -- Some severe problem with the transaction
    IF (XACT_STATE()) <> 0
    BEGIN
        -- There is still an active transaction that should be rolled back
        PRINT 'Rolling back transaction.';
        ROLLBACK TRANSACTION;
    END;

END CATCH;
GO

답변:


8

이 설정되어 있으면 블록 내부에서 트랜잭션을 커밋 할 수 없습니다 .CATCHXACT_ABORTON

검사 XACT_STATE결과에 따라 1을 반환 할 수 있고 COMMIT트랜잭션 이 가능할 수 있기 때문에 MSDN의 예제는 다소 오해의 소지 가 있습니다 .

IF (XACT_STATE()) = 1
BEGIN
    PRINT 'The transaction is committable.' + 
          ' Committing transaction.'
    COMMIT TRANSACTION;   
END;

그것은 사실이 아니다 XACT_STATE(1 개) 내부 결코 반환하지 않을 CATCH경우 블록 XACT_ABORT으로 설정됩니다 ON.

MSDN 샘플 코드는 주로 설정에 XACT_STATE()관계없이 기능 의 사용을 설명하기위한 것 같습니다 XACT_ABORT. 샘플 코드는 모두 작업에 대한 일반적인만큼 외모 XACT_ABORT로 설정 ON하고를 OFF. XACT_ABORT = ON확인 만하면 IF (XACT_STATE()) = 1불필요 해집니다.


Erland Sommarskog의 SQL Server의 오류 및 트랜잭션 처리에 대한 매우 자세한 기사 세트가 있습니다. 에서 2 부 - 오류의 분류 그는 포괄적 인 테이블을 제공하는 함께 풋 오류의 모든 클래스와 그들이 SQL 서버에서 처리하는 방법과 방법 TRY ... CATCHXACT_ABORT동작을 변경합니다.

+-----------------------------+---------------------------++------------------------------+
|                             |     Without TRY-CATCH     ||        With TRY-CATCH        |
+-----------------------------+-------+-------+-----+-----++------------------+-----+-----+
|              SET XACT_ABORT |  OFF  |  ON   | OFF | ON  ||    ON or OFF     | OFF | ON  |
+-----------------------------+-------+-------+-----+-----++------------------+-----+-----+
| Class Name                  |    Aborts     |   Rolls   ||    Catchable     |   Dooms   |
|                             |               |   Back    ||                  |transaction|
+-----------------------------+-------+-------+-----+-----++------------------+-----+-----+
| Fatal errors                |  Connection   |    Yes    ||       No         |    n/a    |
| Batch-aborting              |     Batch     |    Yes    ||       Yes        |    Yes    |
| Batch-only aborting         |     Batch     | No  | Yes ||       Yes        | No  | Yes |
| Statement-terminating       | Stmnt | Batch | No  | Yes ||       Yes        | No  | Yes |
| Terminates nothing at all   |    Nothing    |    No     ||       Yes        | No  | Yes |
| Compilation: syntax errors  |  (Statement)  |    No     ||       Yes        | No  | Yes |
| Compilation: binding errors | Scope | Batch | No  | Yes || Outer scope only | No  | Yes |
| Compilation: optimisation   |     Batch     |    Yes    || Outer scope only |    Yes    |
| Attention signal            |     Batch     | No  | Yes ||       No         |    n/a    |
| Informational/warning msgs  |    Nothing    |    No     ||       No         |    n/a    |
| Uncatchable errors          |    Varying    |  Varying  ||       No         |    n/a    |
+-----------------------------+-------+-------+-----+-----++------------------+-----+-----+

표의 마지막 열은 질문에 대한 답변입니다. 로 TRY-CATCH와 함께 XACT_ABORT ON트랜잭션이 가능한 모든 경우에 운명을 정한다.

질문의 범위를 벗어난 하나의 메모. ERLAND 말한다,이 일관성 세트에 이유 중 하나 XACT_ABORTON:

저장 프로 시저에 명령을 포함시켜야한다는 권장 사항이 이미 SET XACT_ABORT, NOCOUNT ON있습니다. 위의 표를 보면 XACT_ABORT사실상 높은 수준의 일관성이 있음을 알 수 있습니다. 예를 들어, 트랜잭션은 항상 중단됩니다. 내가 설정 한 경우 다음에, 나는 많은 예를 보여줍니다 XACT_ABORTOFF당신이이 기본 설정을하지 않도록해야하는 이유에 대한 이해를 얻을 수 있도록하는 것이.


7

나는 다르게 접근 할 것이다. XACT_ABORT_ON썰매 망치입니다.보다 세련된 접근 방식을 사용할 수 있습니다. 예외 처리 및 중첩 트랜잭션을 참조하십시오 .

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

이 방법은 가능한 경우 TRY 블록 내부에서 수행 된 작업 만 롤백하고 상태를 TRY 블록에 들어가기 전에 상태로 복원합니다. 이 방법을 사용하면 오류가 발생할 경우 모든 작업을 잃지 않고 커서 반복과 같은 복잡한 처리를 수행 할 수 있습니다. 유일한 단점은 트랜잭션 세이브 포인트를 사용하면 분산 트랜잭션과 같은 세이브 포인트와 호환되지 않는 항목을 사용하지 못하도록한다는 것입니다.


나는 당신의 응답을 주셔서 감사합니다,하지만 문제는 우리가 설정할지 여부를 정말 아닙니다 XACT_ABORT으로 ONOFF.
블라디미르 바라 노프

7

TL; DR / 요약 : 질문의이 부분과 관련하여 :

로 설정되어 있을 때CATCH 커밋 XACT_ABORTON 수있는 트랜잭션으로 컨트롤을 전달할 수있는 경우는 없습니다 .

지금이에서 테스트 꽤 일을하고 난 어떤 경우 찾을 수없는 XACT_STATE()반환 1내부의 CATCH블록 @@TRANCOUNT > 0 의 세션 속성 XACT_ABORT입니다 ON. 실제로 SET XACT_ABORT에 대한 현재 MSDN 페이지에 따르면 :

SET XACT_ABORT가 ON이면 Transact-SQL 문에서 런타임 오류가 발생하면 전체 트랜잭션이 종료되고 롤백됩니다.

그 진술은 당신의 추측과 나의 발견과 일치하는 것 같습니다.

에 대한 MSDN 기사 SET XACT_ABORT에는 트랜잭션 내부의 일부 문이 성공적으로 실행되고 일부 XACT_ABORT가로 설정 되면 실패하는 예가 있습니다.OFF

사실 이지만 이 예제의 문장 은 블록 내에 없습니다TRY . 내에서 그 같은 문장 TRY블록은 여전히 오류를 일으킨 한 후 모든 문에 대한 실행을 방지 할 수 있지만, 그 가정하에 XACT_ABORT입니다 OFF컨트롤이 전달 될 때, CATCH트랜잭션이 여전히 이전의 모든 변경 사항이 오류없이 일어나지 않았다 물리적으로 유효 블록 및 그 욕망의 경우, 커밋, 또는 그들은 롤백 할 수 있습니다. 다른 한편, 만약 XACT_ABORT입니다 ON후 사전 변경 사항이 자동으로 백을 출시하고, 다음 당신은 하나에 선택을 부여한다 : a) 문제 a를ROLLBACK이는 거래가 이미 롤백되어 마이너스 재설정 @@TRANCOUNT으로 인해 상황을 수락하는 것입니다. 0또는 b) 오류가 발생합니다. 선택의 여지가 없습니까?

대한 해당 설명서에서 명확하지 않습니다이 퍼즐에 대한 하나 개의 가능성이 중요한 세부 사항 SET XACT_ABORT이 세션 속성, 심지어 그 예제 코드는, 이후 주변왔다이다 SQL 서버 2000 낳은, (문서의 버전간에 거의 동일) TRY...CATCH이었다 구조를 다시 문서를 찾고, 그리고 (예보고 SQL 서버 2005에 도입 하지 않고TRY...CATCH 사용) XACT_ABORT ON원인에게 즉시 트랜잭션의 롤백을 : 언급에서이 없다는 것을 "커밋"(하시기 바랍니다 노트의 어떤 트랜잭션 상태가 없다 해당 SET XACT_ABORT문서 에서 "커밋 할 수없는"모든 트랜잭션 상태 ).

나는 결론을 내리는 것이 합리적이라고 생각합니다.

  1. TRY...CATCHSQL Server 2005에 구문을 도입 하면 새로운 트랜잭션 상태 (예 : "커밋 할 수 없음")와 XACT_STATE()해당 정보를 얻는 기능이 필요했습니다.
  2. 블록을 체크인 XACT_STATE()하는 CATCH것은 다음 두 가지 모두에 해당하는 경우에만 의미가 있습니다.
    1. XACT_ABORT한다 OFF(다른 XACT_STATE()항상 반환해야 -1하고 @@TRANCOUNT모든 당신이 필요로하는 것)
    2. CATCH블록에 논리가 있거나 호출이 중첩 된 경우 체인의 어딘가에을 수행하는 대신 변경 ( COMMIT또는 DML, DDL 등) 수행합니다 ROLLBACK. (이것은 매우 비정형적인 사용 사례입니다.) ** Microsoft XACT_STATE()대신 비공식 권장 사항 대신 항상 @@TRANCOUNT.
  3. 의 도입 TRY...CATCHSQL Server 2005의 구조는 대부분의 경우, 폐기 된,이 XACT_ABORT ON는 트랜잭션 제어의 큰 학위를 제공으로 (당신이 최소한의 옵션이 세션 속성을 COMMIT제공 XACT_STATE()반환하지 않습니다 -1).
    이 바라 보는 또 다른 방법이며, 이전 SQL Server 2005로 , XACT_ABORT ON오류가 발생했을 때 검사에 비해, 정지 처리에 쉽고 신뢰할 수있는 방법을 제공하는 @@ERROR각 문 다음에.
  4. 에 대한 문서 예제 코드는 XACT_STATE()is 검사 XACT_STATE() = 1시기를 보여주기 때문에 잘못되었거나 잘못 오도 된 것 XACT_ABORT입니다 ON.

긴 부분 ;-)

예, MSDN의 예제 코드는 약간 혼동됩니다 ( @@ TRANCOUNT (롤백) 대 XACT_STATE 참조 ) ;-). 그리고, 나는 (당신에 대해 요구하고있는 이유 : 당신은 심지어에서 "커미터"거래를 할 수 있습니다 이해되지 않는다 그것 때문에 중 하나를 보여줍니다 뭔가 오해의 소지가 느낄 CATCH블록 XACT_ABORT이다 ON), 또는 가능한 경우, 그것을 여전히 원하거나 필요로하지 않을 기술적 가능성에 중점을두고 있으며 필요할 가능성이 높은 이유는 무시합니다.

TRY 블록 내부에 심각한 오류가 있으면 제어 장치가 CATCH로 전달됩니다. 따라서 CATCH 내부에 있으면 트랜잭션에 문제가 있으며 실제로이 경우 할 수있는 유일한 일은 롤백하는 것입니다.

특정 단어와 개념의 의미에 대해 같은 페이지에 있는지 확인하면 도움이 될 것입니다.

  • "심각한 오류": 명확하게하기 위해 TRY ... CATCH 는 대부분의 오류를 포착합니다. 발견되지 않는 항목의 목록은 링크 된 MSDN 페이지의 "TRY… CATCH 구문에 영향을받지 않는 오류"섹션에 나열되어 있습니다.

  • "CATCH 내부에 있으면 트랜잭션 에 문제가 있음을 알고 있습니다"(em phas 가 추가됨) : "트랜잭션"에 의해 명시적인 트랜잭션으로 명령문을 그룹화하여 결정한 논리적 작업 단위 를 의미하는 경우 아마 그렇습니다. 우리 대부분의 DB 직원은 롤백이 "유일한 현명한 일"이라고 동의하는 경향이 있다고 생각합니다. 왜냐하면 우리는 명시 적 트랜잭션을 어떻게, 왜 사용하는지에 대한 비슷한 견해를 가지고 있으며 원자 단위를 구성하는 단계를 생각하기 때문입니다. 일의.

    그러나 명시 적 트랜잭션으로 그룹화되는 실제 작업 단위 를 의미하는 경우 아니오, 트랜잭션 자체에 문제가 있음을 알 수 없습니다. 명시 적으로 정의 된 트랜잭션 내에서 실행 되는 명령문에서 오류가 발생 했다는 것만 알고 있습니다. 그러나 DML 또는 DDL 문이 아닐 수도 있습니다. 그리고 그것이 DML 문이더라도 트랜잭션 자체는 여전히 커밋 가능합니다.

위에서 언급 한 두 가지 점을 감안할 때, 우리는 아마도 당신이 "수행 할 수없는"거래와 당신이 "수행하고 싶지 않은"거래를 구별해야 할 것입니다.

XACT_STATE()반환 1하면 거래가 "확정 가능"하다는 의미이며 COMMIT또는 중에서 선택할 수 있습니다 ROLLBACK. 커밋을 원하지 않을 수도 있지만, 예를 들어, 이해하기 어려운 일부 이유로 원한다면 트랜잭션의 일부가 성공적으로 완료 되었기 때문에 가능할 수 있습니다.

그러나를 XACT_STATE()반환 하면 Transaction의 일부가 잘못된 상태 -1ROLLBACK되었기 때문에 실제로 필요합니다 . 이제 제어가 CATCH 블록으로 전달 된 @@TRANCOUNT경우 Transaction을 커밋 할 수 있더라도 왜 확인하고 싶습니까?

그러나 예제 상단에 표시되면 설정이 XACT_ABORT ON약간 변경됩니다. 정규 오류가 발생 BEGIN TRAN하면 XACT_ABORTis OFF및 XACT_STATE ()가 반환 할 때 제어를 CATCH 블록으로 전달 1합니다. 그러나 XACT_ABORT가 ON이면 'ol 오류에 대해 트랜잭션이 "중지됨"(즉, 무효화 됨)을 XACT_STATE()반환 -1합니다. 이 경우, 확인 쓸모없는 것 같다 XACT_STATE()내에 CATCH는 항상 반환 할 것 같은 블록 -1XACT_ABORT입니다 ON.

그렇다면 무엇 XACT_STATE()입니까? 몇 가지 단서는 다음과 같습니다.

  • TRY...CATCH"커밋 할 수없는 트랜잭션 및 XACT_STATE"섹션의에 대한 MSDN 페이지 에서 다음과 같이 말합니다.

    일반적으로 TRY 블록 외부에서 트랜잭션을 종료하는 오류로 인해 TRY 블록 내부에서 오류가 발생할 때 트랜잭션이 커밋 할 수없는 상태가됩니다.

  • "설명"섹션의 SET XACT_ABORT에 대한 MSDN 페이지 는 다음과 같이 말합니다.

    SET XACT_ABORT가 OFF이면 경우에 따라 오류를 발생시킨 Transact-SQL 문만 롤백되고 트랜잭션 처리가 계속됩니다.

    과:

    SQL Server를 포함한 대부분의 OLE DB 공급자에 대한 암시 적 또는 명시 적 트랜잭션의 데이터 수정 문에 대해 XACT_ABORT를 ON으로 설정해야합니다.

  • "설명"섹션에있는 BEGIN TRANSACTION 의 MSDN 페이지에 다음과 같이 말합니다.

    명령문이 커미트되거나 롤백되기 전에 다음 조치가 수행되면 BEGIN TRANSACTION 문으로 시작된 로컬 트랜잭션이 분산 트랜잭션으로 에스컬레이션됩니다.

    • 연결된 서버에서 원격 테이블을 참조하는 INSERT, DELETE 또는 UPDATE 문이 실행됩니다. 연결된 서버에 액세스하는 데 사용 된 OLE DB 공급자가 ITransactionJoin 인터페이스를 지원하지 않으면 INSERT, UPDATE 또는 DELETE 문이 실패합니다.

가장 적합한 사용법은 연결된 서버 DML 문의 컨텍스트 내에있는 것 같습니다. 그리고 나는 몇 년 전에 이것을 직접 겪었다 고 생각합니다. 모든 세부 사항을 기억하지는 못하지만 원격 서버를 사용할 수없는 것과 관련이 있었고 어떤 이유로 든 오류가 TRY 블록에서 잡히지 않아서 CATCH로 보내지지 않았습니다. 없어야 할 때 COMMIT. 물론, 그 필요가 없다는 문제가 있었다 XACT_ABORT세트를하기 ON보다는 확인에 실패 XACT_STATE(), 또는 아마도 둘 다. 그리고 Linked Servers 및 / 또는 Distributed Transactions를 사용하는 경우 XACT_ABORT ONand / or 를 사용해야한다고 말한 것을 기억합니다 XACT_STATE(). 그러나 지금은 해당 문서를 찾을 수 없습니다. 찾은 경우 링크로 업데이트합니다.

아직도, 나는 여러 가지를 시도했지만 보고 XACT_ABORT ON를 통해 CATCH블록으로 제어를 전달하고 전달 하는 시나리오를 찾을 수 없습니다 .XACT_STATE()1

다음 예제를 사용 XACT_ABORT하여 값에 미치는 영향을 확인하십시오 XACT_STATE().

SET XACT_ABORT OFF;

BEGIN TRY
    BEGIN TRAN;

    SELECT 1/0 AS [DivideByZero]; -- error, yo!

    COMMIT TRAN;
END TRY
BEGIN CATCH
    SELECT @@TRANCOUNT AS [@@TRANCOUNT],
            XACT_STATE() AS [XactState],
            ERROR_MESSAGE() AS [ErrorMessage]

    IF (@@TRANCOUNT > 0)
    BEGIN
        ROLLBACK;
    END;
END CATCH;

GO ------------------------------------------------

SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRAN;

    SELECT 1/0 AS [DivideByZero]; -- error, yo!

    COMMIT TRAN;
END TRY
BEGIN CATCH
    SELECT @@TRANCOUNT AS [@@TRANCOUNT],
            XACT_STATE() AS [XactState],
            ERROR_MESSAGE() AS [ErrorMessage]

    IF (@@TRANCOUNT > 0)
    BEGIN
        ROLLBACK;
    END;
END CATCH;

GO ------------------------------------------------

SET XACT_ABORT ON;

BEGIN TRY
    SELECT 1/0 AS [DivideByZero]; -- error, yo!
END TRY
BEGIN CATCH
    SELECT @@TRANCOUNT AS [@@TRANCOUNT],
            XACT_STATE() AS [XactState],
            ERROR_MESSAGE() AS [ErrorMessage]
END CATCH;

최신 정보

이 질문에 대한 다음 의견을 바탕으로 원래 질문의 일부는 아니지만 :

나는에 ERLAND의 기사를 읽어 봤는데 오류 처리 및 트랜잭션 그가 그 말하는 곳 XACT_ABORT이다 OFF레거시 이유로 기본적으로 정상적으로 우리가 그것을 설정해야합니다 ON.
...
"... 권장 사항을 따르고 SET XACT_ABORT ON으로 실행하면 트랜잭션이 항상 중단됩니다."

XACT_ABORT ON어디서나 사용하기 전에 , 나는 여기에서 정확히 무엇을 얻고 있습니까? 나는 당신이 그것을 필요할 때만 사용해야한다고 일반적으로 주장하지 않았다. ROLLBACK@Remus의 답변에 표시된 템플릿을 사용하여 쉽게 처리 할 수 있는지 여부 또는 본질적으로 동일하지만 세이브 포인트가없는 수년 동안 사용한 템플릿을 사용하여 쉽게 처리 할 수 있는지 여부 는 중첩 된 호출을 처리합니다) :

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


업데이트 2

이번에는 작은 .NET 콘솔 앱을 만들고, SqlCommand객체 를 실행하기 전에 (예 :을 통해 using (SqlTransaction _Tran = _Connection.BeginTransaction()) { ...) 응용 프로그램 계층에서 트랜잭션을 생성 하고 명령문 대신 일괄 중단 오류를 사용하여 조금 더 테스트를 수행 했습니다. 오류가 발생하여 다음을 발견했습니다.

  1. "커밋 할 수없는"트랜잭션은 대부분 이미 롤백되었지만 (변경 사항은 취소되었습니다) @@TRANCOUNT여전히 0 보다 큰 트랜잭션입니다 .
  2. "커밋 할 수없는"트랜잭션이있는 경우 트랜잭션을 "커밋 할 수 없음 COMMIT"이라는 오류 메시지가 생성되고 오류가 발생할 수 있습니다 . 또한 배치가 지연되고 커밋 할 수없는 트랜잭션으로 완료되고 롤백 될 것이라는 오류가 발생하고 롤백됩니다 (음, 어쨌든 자동 롤백하는 경우, 왜 오류가 발생합니까?). 따라서 즉시 블록이 아닌 배치가 끝나기 전에 explicit을 발행 해야합니다 .ROLLBACKCATCH
  3. A의 TRY...CATCH경우 구조, XACT_ABORT이다 OFF, 트랜잭션을 종료 할 오류는 자동으로이 외부에서 발생했다 TRY"uncommitable"로 떠나는 Tranasction을 종료 작업을 취소 아닌 것 같은 배치 중단 오류로, 블록. a를 발행하는 ROLLBACK것은 거래를 종결하는 데 필요한 공식적인 것이지만 작업은 이미 롤백되었습니다.
  4. 경우 XACT_ABORT이고 ON, 대부분의 오류는 배치 중단 역할 직접 위 (# 3) 총알 시점에 기재된 따라서 동작.
  5. XACT_STATE()CATCH블록 에 적어도 -1오류가 발생했을 때 활성화 된 트랜잭션이있는 경우 일괄 중단 오류에 대해 표시됩니다.
  6. XACT_STATE()1활성 트랜잭션이없는 경우에도 때때로 반환 됩니다. 경우 @@SPID(다른 사람의 사이에서)가에 SELECT함께 목록 XACT_STATE(), 다음 XACT_STATE()활성 트랜잭션이없는 경우 1을 반환합니다. 이 동작은 SQL Server 2012에서 시작하여 2014 년에 존재하지만 2016 년에는 테스트하지 않았습니다.

위의 사항을 명심하십시오.

  • 주어진 점 오류 "uncommitable"트랜잭션을 렌더링하는 대부분의 (또는 모든?) 때문에 # 4, # 5, 그것은 확인 완전히 무의미한 것 같다 XACT_STATE()CATCH경우 블록 XACT_ABORT입니다 ON항상 될 것입니다 반환 된 값 때문에 -1.
  • 확인 XACT_STATE()CATCH경우 블록 XACT_ABORT이다이 OFF그것을 반환하기 때문에 반환 값이 적어도 어떤 변화를 가지고 있기 때문에 더 의미가 1문-중단 오류. 그러나 대부분의 코드를 코딩 ROLLBACK하는 경우 오류가 발생했다는 사실 때문에 어쨌든 전화 할 것이기 때문에이 구별은 의미가 없습니다 .
  • 당신은 영장이 발행 않는 상황을 발견하면 COMMITCATCH블록을, 다음 의 값을 확인 XACT_STATE()하고,을해야합니다 SET XACT_ABORT OFF;.
  • XACT_ABORT ONTRY...CATCH구조에 비해 이점이 거의 없거나 전혀없는 것 같습니다 .
  • 검사 XACT_STATE()가 단순히 검사보다 의미있는 이점을 제공하는 시나리오는 찾을 수 없습니다 @@TRANCOUNT.
  • 또한 is 일 때 블록 에서 XACT_STATE()반환 1되는 시나리오를 찾을 수 없습니다 . 문서 오류라고 생각합니다.CATCHXACT_ABORTON
  • 예, 명시 적으로 시작하지 않은 트랜잭션을 롤백 할 수 있습니다. XACT_ABORT ON그리고을 사용하는 상황 에서는 TRY블록 에서 발생하는 오류 가 자동으로 변경 사항을 롤백하기 때문에 문제가됩니다.
  • TRY...CATCH구문은 XACT_ABORT ON전체 트랜잭션을 자동으로 취소하지 않기 때문에 이점이 있습니다 ( XACT_STATE()반환 1인 경우).

XACT_STATE()반환 -1할 때 XACT_ABORT입니다 OFF:

SET XACT_ABORT OFF;

BEGIN TRY
    BEGIN TRAN;

    SELECT CONVERT(INT, 'g') AS [ConversionError];

    COMMIT TRAN;
END TRY
BEGIN CATCH
    DECLARE @State INT;
    SET @State = XACT_STATE();
    SELECT @@TRANCOUNT AS [@@TRANCOUNT],
            @State AS [XactState],
            ERROR_MESSAGE() AS [ErrorMessage];

    IF (@@TRANCOUNT > 0)
    BEGIN
        SELECT 'Rollin back...' AS [Transaction];
        ROLLBACK;
    END;
END CATCH;

업데이트 3

UPDATE 2 섹션의 6 번 항목과 관련이 있습니다 (즉, XACT_STATE()활성화 된 트랜잭션이 없을 때 반환되는 잘못된 값 ).

  • 이상한 / 잘못된 동작이 SQL Server 2012에서 시작되었습니다 (지금까지 2012 SP2 및 2014 SP1에 대해 테스트 됨)
  • SQL Server 버전 2005, 2008 및 2008 R2에서는 XACT_STATE()트리거 또는 INSERT...EXEC시나리오 에서 사용될 때 예상 값을보고하지 않았습니다 . xact_state ()를 사용하여 트랜잭션이 중단되었는지 여부를 확인할 수 없습니다 . 그러나이 3 버전 (2008 R2에서만 테스트 됨) 에서 with와 함께 사용될 때 잘못보고 XACT_STATE()하지 않습니다 .1SELECT@@SPID
  • 여기에 언급 된 동작에 대해 제출 된 Connect 버그가 있지만 "디자인으로"닫힙니다. XACT_STATE ()는 SQL 2012에서 잘못된 트랜잭션 상태를 반환 할 수 있습니다 . 그러나 테스트는 DMV에서 선택할 때 수행되었으며, 그렇게하면 최소한 일부 DMV에 대해 시스템 생성 트랜잭션이 자연스럽게 발생한다고 결론을 내 렸습니다. 또한 MS의 최종 응답에서 다음과 같이 언급되었습니다.

    IF 문과 FROM이없는 SELECT는 트랜잭션을 시작하지 않습니다.
    예를 들어, 기존 트랜잭션이없는 경우 SELECT XACT_STATE ()를 실행하면 0이 반환됩니다.

    다음 예에서는 이러한 문장이 올바르지 않습니다.

    SELECT @@TRANCOUNT AS [TRANCOUNT], XACT_STATE() AS [XACT_STATE], @@SPID AS [SPID];
    GO
    DECLARE @SPID INT;
    SET @SPID = @@SPID;
    SELECT @@TRANCOUNT AS [TRANCOUNT], XACT_STATE() AS [XACT_STATE], @SPID AS [SPID];
    GO
  • 따라서 새로운 연결 버그 :
    XACT_STATE ()는 SELECT에서 일부 시스템 변수를 사용하지만 FROM 절을 사용하지 않으면 1을 반환합니다.

"XACT_STATE ()에서 SQL 2012에서 잘못된 트랜잭션 상태를 반환 할 수 있음"에서 바로 위에 연결된 Microsoft Connect 항목을 참조하십시오.

@@ trancount는 BEGIN TRAN 문의 수를 반환합니다. 따라서 활성 트랜잭션이 있는지 여부를 신뢰할 수있는 지표가 아닙니다. 활성 자동 커밋 트랜잭션이있는 경우 XACT_STATE ()도 1을 반환하므로 활성 트랜잭션이 있는지 여부를보다 신뢰할 수있는 지표입니다.

그러나 신뢰할 수없는 이유는 없습니다 @@TRANCOUNT. 다음 테스트는 @@TRANCOUNT실제로 1자동 커밋 트랜잭션에서 반환 되는 것을 보여줍니다 .

--- begin setup
GO
CREATE PROCEDURE #TransactionInfo AS
SET NOCOUNT ON;
SELECT @@TRANCOUNT AS [TranCount],
       XACT_STATE() AS [XactState];
GO
--- end setup

DECLARE @Test TABLE (TranCount INT, XactState INT);

SELECT * FROM @Test; -- no rows

EXEC #TransactionInfo; -- 0 for both fields

INSERT INTO @Test (TranCount, XactState)
    EXEC #TransactionInfo;

SELECT * FROM @Test; -- 1 row; 1 for both fields

또한 트리거를 사용하여 실제 테이블에서 테스트했으며 명시 적 트랜잭션이 시작되지 않았더라도 @@TRANCOUNT트리거 내에서 정확하게보고 1했습니다.


4

방어 프로그래밍을 위해서는 가능한 많은 알려진 상태를 처리하는 코드를 작성해야하므로 버그 가능성이 줄어 듭니다.

롤백을 실행할 수 있는지 확인하기 위해 XACT_STATE ()를 확인하는 것이 좋은 방법입니다. 맹목적으로 롤백을 시도하면 TRY ... CATCH 내부에 실수로 오류가 발생할 수 있습니다.

TRY ... CATCH 내에서 롤백이 실패하는 한 가지 방법은 트랜잭션을 명시 적으로 시작하지 않은 경우입니다. 코드 블록을 복사하여 붙여 넣으면 쉽게 발생할 수 있습니다.


답장을 보내 주셔서 감사합니다. 나는 단순한 ROLLBACK것이 내부에서 작동하지 않을 때를 생각할 수 없었고 CATCH당신은 좋은 모범을 보였습니다. 중첩 된 트랜잭션과 중첩 된 저장 프로 시저 TRY ... CATCH ... ROLLBACK가 포함 되어 있으면 신속하게 혼란 스러울 수 있습니다 .
블라디미르 바라 노프

그럼에도 불구하고 두 번째 부분에 대한 답변을 연장 할 수 있다면 고맙겠습니다. 커밋 가능한 트랜잭션으로 어떻게 블록 IF (XACT_STATE()) = 1 COMMIT TRANSACTION; 안에서 끝낼 수 CATCH있습니까? 나는 내부에서 (가능한) 쓰레기를 커밋하지 않을 것 CATCH입니다. 내 추론은 : 만약 우리가 CATCH뭔가 잘못되면 데이터베이스의 상태를 신뢰할 수 없기 때문에 ROLLBACK우리가 가진 모든 것이 더 나을 것 입니다.
블라디미르 바라 노프
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.