TL; DR / 요약 : 질문의이 부분과 관련하여 :
로 설정되어 있을 때CATCH
커밋 할XACT_ABORT
ON
수있는 트랜잭션으로 컨트롤을 전달할 수있는 경우는 없습니다 .
지금이에서 테스트 꽤 일을하고 난 어떤 경우 찾을 수없는 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
문서 에서 "커밋 할 수없는"모든 트랜잭션 상태 ).
나는 결론을 내리는 것이 합리적이라고 생각합니다.
TRY...CATCH
SQL Server 2005에 구문을 도입 하면 새로운 트랜잭션 상태 (예 : "커밋 할 수 없음")와 XACT_STATE()
해당 정보를 얻는 기능이 필요했습니다.
- 블록을 체크인
XACT_STATE()
하는 CATCH
것은 다음 두 가지 모두에 해당하는 경우에만 의미가 있습니다.
XACT_ABORT
한다 OFF
(다른 XACT_STATE()
항상 반환해야 -1
하고 @@TRANCOUNT
모든 당신이 필요로하는 것)
CATCH
블록에 논리가 있거나 호출이 중첩 된 경우 체인의 어딘가에을 수행하는 대신 변경 ( COMMIT
또는 DML, DDL 등) 을 수행합니다 ROLLBACK
. (이것은 매우 비정형적인 사용 사례입니다.) ** Microsoft XACT_STATE()
대신 비공식 권장 사항 대신 항상 @@TRANCOUNT
.
- 의 도입
TRY...CATCH
SQL Server 2005의 구조는 대부분의 경우, 폐기 된,이 XACT_ABORT ON
는 트랜잭션 제어의 큰 학위를 제공으로 (당신이 최소한의 옵션이 세션 속성을 COMMIT
제공 XACT_STATE()
반환하지 않습니다 -1
).
이 바라 보는 또 다른 방법이며, 이전 SQL Server 2005로 , XACT_ABORT ON
오류가 발생했을 때 검사에 비해, 정지 처리에 쉽고 신뢰할 수있는 방법을 제공하는 @@ERROR
각 문 다음에.
- 에 대한 문서 예제 코드는
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의 일부가 잘못된 상태 -1
가 ROLLBACK
되었기 때문에 실제로 필요합니다 . 이제 제어가 CATCH 블록으로 전달 된 @@TRANCOUNT
경우 Transaction을 커밋 할 수 있더라도 왜 확인하고 싶습니까?
그러나 예제 상단에 표시되면 설정이 XACT_ABORT ON
약간 변경됩니다. 정규 오류가 발생 BEGIN TRAN
하면 XACT_ABORT
is OFF
및 XACT_STATE ()가 반환 할 때 제어를 CATCH 블록으로 전달 1
합니다. 그러나 XACT_ABORT가 ON
이면 'ol 오류에 대해 트랜잭션이 "중지됨"(즉, 무효화 됨)을 XACT_STATE()
반환 -1
합니다. 이 경우, 확인 쓸모없는 것 같다 XACT_STATE()
내에 CATCH
는 항상 반환 할 것 같은 블록 -1
때 XACT_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 ON
and / 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()) { ...
) 응용 프로그램 계층에서 트랜잭션을 생성 하고 명령문 대신 일괄 중단 오류를 사용하여 조금 더 테스트를 수행 했습니다. 오류가 발생하여 다음을 발견했습니다.
- "커밋 할 수없는"트랜잭션은 대부분 이미 롤백되었지만 (변경 사항은 취소되었습니다)
@@TRANCOUNT
여전히 0 보다 큰 트랜잭션입니다 .
- "커밋 할 수없는"트랜잭션이있는 경우 트랜잭션을 "커밋 할 수 없음
COMMIT
"이라는 오류 메시지가 생성되고 오류가 발생할 수 있습니다 . 또한 배치가 지연되고 커밋 할 수없는 트랜잭션으로 완료되고 롤백 될 것이라는 오류가 발생하고 롤백됩니다 (음, 어쨌든 자동 롤백하는 경우, 왜 오류가 발생합니까?). 따라서 즉시 블록이 아닌 배치가 끝나기 전에 explicit을 발행 해야합니다 .ROLLBACK
CATCH
- A의
TRY...CATCH
경우 구조, XACT_ABORT
이다 OFF
, 트랜잭션을 종료 할 오류는 자동으로이 외부에서 발생했다 TRY
"uncommitable"로 떠나는 Tranasction을 종료 작업을 취소 아닌 것 같은 배치 중단 오류로, 블록. a를 발행하는 ROLLBACK
것은 거래를 종결하는 데 필요한 공식적인 것이지만 작업은 이미 롤백되었습니다.
- 경우
XACT_ABORT
이고 ON
, 대부분의 오류는 배치 중단 역할 직접 위 (# 3) 총알 시점에 기재된 따라서 동작.
XACT_STATE()
CATCH
블록 에 적어도 -1
오류가 발생했을 때 활성화 된 트랜잭션이있는 경우 일괄 중단 오류에 대해 표시됩니다.
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
하는 경우 오류가 발생했다는 사실 때문에 어쨌든 전화 할 것이기 때문에이 구별은 의미가 없습니다 .
- 당신은 영장이 발행 않는 상황을 발견하면
COMMIT
에 CATCH
블록을, 다음 의 값을 확인 XACT_STATE()
하고,을해야합니다 SET XACT_ABORT OFF;
.
XACT_ABORT ON
TRY...CATCH
구조에 비해 이점이 거의 없거나 전혀없는 것 같습니다 .
- 검사
XACT_STATE()
가 단순히 검사보다 의미있는 이점을 제공하는 시나리오는 찾을 수 없습니다 @@TRANCOUNT
.
- 또한 is 일 때 블록 에서
XACT_STATE()
반환 1
되는 시나리오를 찾을 수 없습니다 . 문서 오류라고 생각합니다.CATCH
XACT_ABORT
ON
- 예, 명시 적으로 시작하지 않은 트랜잭션을 롤백 할 수 있습니다.
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()
하지 않습니다 .1
SELECT
@@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
했습니다.
XACT_ABORT
으로ON
나OFF
.