SQL Server는 TRY...CATCH 구조를 사용하여 TSQL 문의 오류 처리를 구현합니다. TRY 명령은 문 실행을 모니터링하고, TSQL 문에서 심각도 수준(severity level)이 10보다 크고 20보다 작은 예외가 발생하면 CATCH 명령이 예외 오류를 포착합니다.
BEGIN TRY
{ sql_statement | statement_block }
END TRY
BEGIN CATCH
[ { sql_statement | statement_block } ]
END CATCH
데이터베이스 개발자는 하나 이상의 문을 TRY 코드 블록에 작성해야 합니다. TRY 블록 내의 코드가 실행 중 오류를 발생시키면, 오류가 발생한 지점 이후의 코드는 실행되지 않고 프로그램 제어권이 CATCH 블록으로 이동합니다. TRY 블록에 오류가 없으면 CATCH 블록이 실행되지 않고, 프로그램 제어권은 END CATCH 이후의 문장으로 넘어갑니다.
참고: CATCH 블록에서 포착된 예외는 호출 프로그램으로 반환되지 않습니다. 호출 프로그램에 오류 메시지를 반환하려면 CATCH 블록 내에서 THROW(또는 RAISERROR, 권장되지 않음) 명령을 사용하여 오류를 명시적으로 던져야 합니다.
1. 예외 메시지 가져오기
TSQL에서는 TRY와 CATCH를 사용하여 예외 처리 코드 블록을 작성합니다. CATCH 절에서 다음 함수를 사용하면 예외 발생 시 정보를 얻을 수 있습니다.
-- 오류가 발생한 코드 줄 번호 반환
ERROR_LINE ( )
-- 오류 번호 반환
ERROR_NUMBER ( )
@@ERROR
-- 오류 메시지 반환
ERROR_MESSAGE ( )
-- 오류가 발생한 SP 이름 반환
ERROR_PROCEDURE ( )
-- 오류 심각도 반환
ERROR_SEVERITY ( )
-- 오류 상태 반환
ERROR_STATE()
SQL Server에서 던져진 오류는 일반적으로 오류 코드(Error Number), 심각도 수준(Severity Level), 오류 상태(Error State), 오류 메시지(Error Message) 등의 정보를 포함합니다.
1.1 오류 코드
오류 코드는 @@ERROR 변수와 ERROR_NUMBER() 함수로 얻을 수 있으며, 마지막 문장의 오류 코드를 반환합니다. 이 코드는 오류를 고유하게 식별합니다.
1.2 오류 심각도 수준
심각도 수준(Severity Level)은 24단계로 구성되며, SQL Server가 직면한 문제 유형을 나타냅니다. int 유형이며 ERROR_SEVERITY() 함수로 반환되며, 값이 클수록 문제가 더 심각함을 의미합니다.
심각도 수준은 시스템에 미치는 영향에 따라 네 그룹으로 분류됩니다:
- 0-10: 정보 수준, 경고(warning)로 간주
- 11-16: 오류, 사용자 코드로 인해 발생
- 17-19: 매우 심각한 오류, 시스템 관리자만 수정 가능
- 20-24: 치명적 오류, 전체 시스템이 정상적으로 작동하지 못할 수 있음
17-19: 사용자가 수정할 수 없으며 시스템 관리자만 문제를 해결할 수 있습니다.
20-24: 이 수준의 오류는 드물게 발생하며, 발생 시 데이터베이스 시스템이 매우 심각한 오류에 직면했음을 의미합니다.
1.3 오류 상태
오류 상태(Error State)는 사용자 정의 코드로, 개발자가 예외의 정확한 위치를 쉽게 식별할 수 있도록 합니다.
1.4 오류 메시지
오류 메시지는 오류에 대한 설명 텍스트로, SQL Server 시스템 사전 정의 오류 메시지이거나 THROW 명령으로 던져진 사용자 정의 텍스트일 수 있습니다.
2. 예외 메시지 던지기
SQL Server 2012 이상 버전에서는 RAISERROR 대신 THROW 키워드를 사용하여 예외를 던지고 실행 제어권을 CATCH 블록으로 전달합니다.
THROW [error_number, error_message, error_state];
매개변수 설명:
- error_number: 오류 코드, int 유형이며 5000보다 크고 2147483647보다 작아야 합니다. 사용자 정의 오류 코드입니다.
- error_message: 오류 메시지, nvarchar(2048) 유형
- state: 오류와 관련된 상태, tinyint 유형, 범위는 0-255
참고: THROW 문 앞의 문장은 세미콜론(;)으로 끝나야 합니다.
THROW 문이 사용자 정의 예외를 던질 때, 심각도 수준(Severity Level)은 일반적으로 기본값 16으로 설정됩니다. THROW가 재던지기(re-throw)에 사용될 때는 매개변수 없이 CATCH 블록 내에서 사용되며, 포착된 예외를 다시 던집니다. 이때 심각도 수준, 상태, 오류 메시지는 원래 예외와 동일합니다.
예제 1: 사용자 정의 예외 던지기:
BEGIN TRY
SELECT 1/0
END TRY
BEGIN CATCH
;THROW 51000, 'Divide by zero error encountered', 1;
END CATCH;
SQL Server가 던진 예외 메시지: 사용자 정의 오류 코드는 51000, 심각도 수준은 16, 오류 상태는 1, 오류 줄은 5:
Msg 51000, Level 16, State 1, Line 5
Divide by zero error encountered
예제 2: 재던지기, 시스템 감지 오류를 CATCH 블록에서 던집니다:
BEGIN TRY
SELECT 1/0
END TRY
BEGIN CATCH
;THROW;
END CATCH;
SQL Server가 던진 예외 메시지: 오류 코드는 8134, 심각도 수준은 16, 오류 상태는 1, 오류 줄은 2:
Msg 8134, Level 16, State 1, Line 2
Divide by zero error encountered.
3. TRY...CATCH 구조의 영향을 받지 않는 오류
TRY...CATCH는 심각도 수준 11~19의 오류만 포착하며, 심각도 수준 1-10 및 20-24의 오류는 포착하지 않습니다.
세션이 시스템 관리자에 의해 KILL 명령으로 종료되면 TRY...CATCH 구조가 이를 포착하지 않습니다.
4. 트랜잭션에서 예외 처리
TRY 코드 블록에서 생성된 오류로 인해 현재 트랜잭션 상태가 무효화되면, 해당 트랜잭션은 커밋할 수 없는 트랜잭션(uncommittable transaction)이 됩니다. 커밋할 수 없는 트랜잭션은 읽기 작업이나 롤백(ROLLBACK TRANSACTION)만 수행할 수 있으며, 쓰기 작업을 수행하는 TSQL 문을 실행하거나 커밋할 수 없습니다. XACT_STATE() 함수는 -1을 반환하여 현재 트랜잭션이 커밋할 수 없음을 나타내고, 1을 반환하면 커밋할 수 있음을 나타냅니다. 데이터베이스 개발자는 XACT_STATE()를 사용하여 트랜잭션을 커밋하거나 롤백해야 합니다.
예를 들어, 트랜잭션에서 예외를 처리하는 코드는 다음과 같습니다. 실제 애플리케이션에서는 예외 정보를 데이터 테이블에 기록하여 문제 해결에 사용할 수 있습니다:
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRANSACTION;
DELETE FROM Production.Product
WHERE ProductID = 980;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF (XACT_STATE()) = -1
BEGIN
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_STATE() AS ErrorState,
ERROR_PROCEDURE() AS ErrorProcedure,
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage;
ROLLBACK TRANSACTION;
END;
IF (XACT_STATE()) = 1
BEGIN
COMMIT TRANSACTION;
END;
END CATCH;