거래의 경우
- A 의 인벤토리에서 아이템 제거
- B의 인벤토리에 아이템 추가
- A의 골드 감소
이렇게 모두 처리되야 하는데 중간에 실패가 난다면 완전히 거래가 처리 되는것이 아니고 오류가 되어버리는데
이렇게 되면 테이블 데이터가 잘못되게 된다, 강화 하는 경우에도 비슷한 케이스
All or Nothing
이런걸 해결 하기위해 TRANSACTION 이 있다
아무것도 쓰지 않으면 기본 적으로 TRANSACTION 이 있고 그 뒤에 COMMIT 이 있는 것인데 (EX : INSERT INTO ...)
BEGIN TRAN; 을 명시하면 뒤에 COMMIT 또는 ROLLBACK 을 적어 처리할지 되돌릴지를 정할 수 있다
INSERT INTO accounts VALUES(1, 'TESET STR', 100, GETUTCDATE());
BEGIN TRAN;
INSERT INTO accounts VALUES(2, 'TESET STR', 100, GETUTCDATE());
ROLLBACK;
결과를 보면 두번째 것은 추가 안된것을 알수 있다 ROLLBACK 되었음으로
INSERT INTO accounts VALUES(1, 'TESET STR', 100, GETUTCDATE());
BEGIN TRAN;
INSERT INTO accounts VALUES(2, 'TESET STR', 100, GETUTCDATE());
COMMIT;
추가된 경우
아래 구문을 실행하기 전 데이터
--TRY CATCH 와 비슷한 구문
BEGIN TRY
BEGIN TRAN;
INSERT INTO accounts VALUES(1, 'T1', 100, GETUTCDATE());
INSERT INTO accounts VALUES(2, 'T2', 100, GETUTCDATE());
COMMIT;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK;
END CATCH
@@TRANCOUNT 는 TRAN 이 몇개 인지 알수 있는 매크로인데
BEGIN TRAN;
TRAN;
이렇게 TRAN 을 중첩시키면 1이상이 될 수가 있는데 이때 이 개수를 리턴해주는게 @@TRANCOUNT 이다
그리고 INSERT 는 accountId 가 primary key 임으로 이미 존재하는 키가 있을 경우 또다시 추가 하려고 하면 에러가 발생되어 catch 로 잡히게 된다
실행후 상황
아무 영향이 없었다는 걸 알 수 있다
PRINT('TTT') 를 ROLLBACK 구문 쪽에 써서 ROLLBACK 된 이유를 적어줄 수도 있다
중요한건 TRAN 으로 묶어놓은건 한번에 다 실행하거나 아니면 중간에 오류/예외가 발생하면 모두 실행되지 않는 다는 것이다
그리고 TRANSACTION 은 보통 두개 이상의 테이블에 어떤 변경이나 영향을 줄때 사용된다
사용시 주의 할점은
TRAN 안에는 꼭 원자적으로 실행될 애들만 넣어야 한다
즉 성능적으로 문제가 될 수 있기 때문인데
LOCK이 되기 때문
만약
BEGIN TRAN;
INSERT INTO accounts VALUES(2, 'TESET STR', 100, GETUTCDATE());
이렇게 까지 되어 있으면 위코드는 COMMIT 이나 ROLLBACK 을 만나기 전까지 계속 LOCK 리 걸린 상태가 되며
다른 구문에서 accounts 를 조회하려는 구문을 실행한다 해도 select 구문은 실행 되지 않고 계속 대기하게 된다
대기상태에 빠지게 됨 commit 이나 rollback 을 만나지 않는다면 원자적 특성때문에
같은 얘기지만 TRAN 과 COMMIT/ROLLBACK 사이의 구문은 길지 않게 작성하는 것이 좋다
'서버(Server) > DB' 카테고리의 다른 글
DB : BATCH : GO (0) | 2023.02.19 |
---|---|
DB : 변수 (0) | 2023.02.18 |
DB : INNER JOIN, CROSS JOIN (0) | 2023.02.16 |
DB : UNION : 합치면서 중복제거, INTERSECT 교집합, EXCEPT 차집합 (0) | 2023.02.15 |
DB : 인덱스(Index) (0) | 2023.02.13 |