Transact-SQL에서 TRY...CATCH 사용

업데이트: 2006년 7월 17일

Transact-SQL 코드의 오류는 Microsoft Visual C++ 및 Microsoft Visual C# 언어의 예외 처리 기능과 비슷한 TRY...CATCH 구문을 사용하여 처리할 수 있습니다. TRY...CATCH 구문은 TRY 블록과 CATCH 블록의 두 부분으로 구성됩니다. TRY 블록 내에 있는 Transact-SQL 문에서 오류 조건을 감지하면 오류를 처리할 수 있는 CATCH 블록으로 제어가 넘어갑니다.

CATCH 블록에서 예외를 처리한 후에는 END CATCH 문 뒤의 첫 번째 Transact-SQL 문으로 제어가 넘어갑니다. END CATCH 문이 저장 프로시저나 트리거의 마지막 문인 경우에는 저장 프로시저나 트리거를 호출한 코드로 제어가 반환됩니다. TRY 블록에서 오류를 생성하는 문 뒤의 Transact-SQL 문은 실행되지 않습니다.

TRY 블록 내에 오류가 없으면 연결된 END CATCH 문 바로 뒤의 문으로 제어가 넘어갑니다. END CATCH 문이 저장 프로시저나 트리거의 마지막 문인 경우에는 저장 프로시저나 트리거를 호출한 문으로 제어가 넘어갑니다.

TRY 블록은 BEGIN TRY 문으로 시작해서 END TRY 문으로 끝납니다. BEGIN TRY 문과 END TRY 문 사이에 하나 이상의 Transact-SQL 문을 지정할 수 있습니다.

TRY 블록 바로 뒤에는 CATCH 블록이 와야 합니다. CATCH 블록은 BEGIN CATCH 문으로 시작해서 END CATCH 문으로 끝납니다. Transact-SQL에서 각 TRY 블록은 하나의 CATCH 블록과만 연결됩니다.

TRY...CATCH 사용

다음은 TRY...CATCH 구문을 사용할 때 고려해야 할 지침과 제안 사항입니다.

  • 각 TRY...CATCH 구문은 하나의 일괄 처리, 저장 프로시저 또는 트리거 내에 있어야 합니다. 예를 들어 TRY 블록 및 연결된 CATCH 블록은 서로 다른 일괄 처리에 넣을 수 없습니다. 다음은 오류를 생성하는 스크립트입니다.

    BEGIN TRY
        SELECT *
            FROM sys.messages
            WHERE message_id = 21;
    END TRY
    GO
    -- The previous GO breaks the script into two batches,
    -- generating syntax errors. The script runs if this GO
    -- is removed.
    BEGIN CATCH
        SELECT ERROR_NUMBER() AS ErrorNumber;
    END CATCH;
    GO
    
  • TRY 블록 바로 뒤에는 CATCH 블록이 와야 합니다.

  • TRY...CATCH 구문은 중첩할 수 있습니다. 즉, 다른 TRY 및 CATCH 블록 내에 TRY...CATCH 구문을 넣을 수 있습니다. 중첩된 TRY 블록 내에서 오류가 발생하면 중첩된 TRY 블록과 연결된 CATCH 블록으로 프로그램 제어가 넘어갑니다.

  • 특정 CATCH 블록 내에서 발생하는 오류를 처리하려면 해당 CATCH 블록 내에 TRY...CATCH 블록을 작성하십시오.

  • 데이터베이스 엔진에서 연결을 닫도록 하는 심각도 20 이상의 오류는 TRY...CATCH 블록에서 처리되지 않습니다. 그러나 연결이 닫히지만 않으면 TRY...CATCH 블록에서 심각도 20 이상의 오류를 처리합니다.

  • 심각도 10 이하의 오류는 경고 또는 정보 메시지로 간주되어 TRY...CATCH 블록에서 처리되지 않습니다.

  • 주의는 일괄 처리가 TRY...CATCH 구문의 범위 내에 있더라도 일괄 처리를 종료합니다. 여기에는 분산 트랜잭션 실패 시 MS DTC(Microsoft Distributed Transaction Coordinator)에서 보내는 주의가 포함됩니다. MS DTC에서는 분산 트랜잭션을 관리합니다.

    [!참고] 분산 트랜잭션이 TRY 블록 범위 내에서 실행되어 오류가 발생하면 연결된 CATCH 블록으로 실행이 넘어갑니다. 분산 트랜잭션은 커밋할 수 없는 상태가 됩니다. CATCH 블록 내의 실행은 분산 트랜잭션을 관리하는 Microsoft Distributed Transaction Coordinator에서 중단할 수 있습니다. 오류가 발생하면 MS DTC에서 분산 트랜잭션에 참여하는 모든 서버에 비동기적으로 알리고 분산 트랜잭션과 관련된 모든 작업을 종료합니다. 이 알림은 주의의 형태로 전송되고 TRY...CATCH 구문에서 처리되지 않으며 일괄 처리가 종료됩니다. 일괄 처리 실행이 완료되면 데이터베이스 엔진은 커밋할 수 없는 활성 트랜잭션을 모두 롤백합니다. 트랜잭션이 커밋할 수 없는 상태가 되었을 때 오류 메시지가 전송되지 않은 경우 일괄 처리가 완료되면 커밋할 수 없는 트랜잭션이 검색되어 롤백되었음을 나타내는 오류 메시지가 클라이언트 응용 프로그램으로 전송됩니다. 분산 트랜잭션에 대한 자세한 내용은 분산 트랜잭션(데이터베이스 엔진)을 참조하십시오.

오류 함수

TRY...CATCH에서는 다음 오류 함수를 사용하여 오류 정보를 캡처합니다.

  • ERROR_NUMBER()는 오류 번호를 반환합니다.
  • ERROR_MESSAGE()는 오류 메시지의 전체 텍스트를 반환합니다. 이 텍스트는 길이, 개체 이름 또는 시간과 같은 대체 가능한 매개 변수에 대해 제공된 값을 포함합니다.
  • ERROR_SEVERITY()는 오류 심각도를 반환합니다.
  • ERROR_STATE()는 오류 상태 번호를 반환합니다.
  • ERROR_LINE()은 오류를 발생시킨 루틴 내의 줄 번호를 반환합니다.
  • ERROR_PROCEDURE()는 오류가 발생한 저장 프로시저 또는 트리거의 이름을 반환합니다.

오류 정보는 TRY...CATCH 구문의 CATCH 블록 범위 어디에서나 이러한 함수를 사용하여 검색됩니다. CATCH 블록 범위 외부에서 오류 함수를 호출하면 NULL이 반환됩니다. 오류 함수는 저장 프로시저 내에서 참조할 수 있으며 CATCH 블록에서 저장 프로시저가 실행될 때 오류 정보를 검색하는 데 사용할 수 있습니다. 이렇게 하면 모든 CATCH 블록에서 오류 처리 코드를 반복하지 않아도 됩니다. 다음 코드 예에서 TRY 블록의 SELECT 문은 0으로 나누기 오류를 생성합니다. 오류는 CATCH 블록에서 처리되며 이 블록에서는 저장 프로시저를 사용하여 오류 정보를 반환합니다.

USE AdventureWorks;
GO

-- Verify that the stored procedure does not exist.
IF OBJECT_ID ('usp_GetErrorInfo', 'P') IS NOT NULL
    DROP PROCEDURE usp_GetErrorInfo;
GO

-- Create a procedure to retrieve error information.
CREATE PROCEDURE usp_GetErrorInfo
AS
    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;
GO

BEGIN TRY
    -- Generate divide-by-zero error.
    SELECT 1/0;
END TRY
BEGIN CATCH
    -- Execute the error retrieval routine.
    EXECUTE usp_GetErrorInfo;
END CATCH;
GO

컴파일 및 문 수준 다시 컴파일 오류

TRY...CATCH 구문과 같은 실행 수준에서 오류가 발생할 경우 TRY...CATCH에서 처리되지 않는 오류 유형에는 다음 두 가지가 있습니다.

  • 컴파일 오류(예: 일괄 처리가 실행되지 않게 하는 구문 오류)
  • 문 수준에서 다시 컴파일하는 동안 발생한 오류(예: 이름 확인 지연으로 인해 컴파일 후 발생한 개체 이름 확인 오류)

TRY...CATCH 구문을 포함하는 일괄 처리, 저장 프로시저 또는 트리거가 이러한 오류 중 하나를 생성하면 TRY...CATCH 구문에서 해당 오류를 처리하지 않습니다. 이러한 오류는 오류 생성 루틴을 호출한 응용 프로그램이나 일괄 처리에 반환됩니다. 예를 들어 다음 코드 예에서는 구문 오류를 발생시키는 SELECT 문을 보여 줍니다. SQL Server Management Studio 쿼리 편집기에서 이 코드를 실행하면 일괄 처리 컴파일이 실패하기 때문에 실행이 시작되지 않습니다. 오류는 쿼리 편집기에 반환되며 TRY…CATCH에서 catch되지 않습니다.

USE AdventureWorks;
GO

BEGIN TRY
    -- This PRINT statement will not run because the batch
    -- does not begin execution.
    PRINT N'Starting execution';

    -- This SELECT statement contains a syntax error that
    -- stops the batch from compiling successfully.
    SELECT ** FROM HumanResources.Employee;
END TRY
BEGIN CATCH
    SELECT 
        ERROR_NUMBER() AS ErrorNumber,
        ERROR_MESSAGE() AS ErrorMessage;
END CATCH;
GO

이전 예의 구문 오류와는 달리 문 수준에서 다시 컴파일하는 동안 발생하는 오류는 일괄 처리의 컴파일을 막는 것이 아니라 문의 다시 컴파일이 실패하면 바로 일괄 처리를 종료합니다. 예를 들어 일괄 처리에 두 개의 문이 있고 두 번째 문이 존재하지 않는 테이블을 참조하면 이름 확인 지연으로 인해 일괄 처리가 성공적으로 컴파일되고 해당 문이 다시 컴파일될 때까지 해당 테이블을 쿼리 계획에 바인딩하지 않은 채 일괄 처리 실행이 시작됩니다. 존재하지 않는 테이블을 참조하는 문에 도달하면 일괄 처리 실행이 중지되고 오류가 반환됩니다. 이 유형의 오류는 오류가 발생한 것과 같은 실행 수준에서 TRY...CATCH 구문이 처리하지 않습니다. 다음 예에서는 이러한 동작을 보여 줍니다.

USE AdventureWorks;
GO

BEGIN TRY
    -- This PRINT statement will run because the error
    -- occurs at the SELECT statement.
    PRINT N'Starting execution';

    -- This SELECT statement will generate an object name
    -- resolution error because the table does not exist.
    SELECT * FROM NonExistentTable;
END TRY
BEGIN CATCH
    SELECT 
        ERROR_NUMBER() AS ErrorNumber,
        ERROR_MESSAGE() AS ErrorMessage;
END CATCH;
GO

TRY...CATCH를 사용하여 컴파일하거나 문 수준에서 다시 컴파일하는 동안 발생한 오류를 처리하는 작업은 TRY 블록 내 별개의 일괄 처리에서 오류 생성 코드를 실행하여 수행할 수 있습니다. 예를 들어 저장 프로시저에 코드를 넣거나 sp_executesql을 사용하는 동적 Transact-SQL 문을 실행하여 이 작업을 수행합니다. 이렇게 하면 TRY...CATCH에서는 오류가 발생한 것보다 높은 실행 수준에서 오류를 catch할 수 있습니다. 예를 들어 다음 코드는 개체 이름 확인 오류를 생성하는 저장 프로시저를 보여 줍니다. TRY…CATCH 구문을 포함하는 일괄 처리는 저장 프로시저보다 높은 수준에서 실행되고 있으며 저장 프로시저보다 낮은 수준에서 발생한 오류가 catch됩니다.

USE AdventureWorks;
GO

-- Verify that the stored procedure does not already exist.
IF OBJECT_ID ('usp_MyError', 'P') IS NOT NULL
    DROP PROCEDURE usp_MyError;
GO

CREATE PROCEDURE usp_MyError
AS
    -- This SELECT statement will generate
    -- an object name resolution error.
    SELECT * FROM NonExistentTable;
GO

BEGIN TRY
    -- Run the stored procedure.
    EXECUTE usp_MyError;
END TRY
BEGIN CATCH
    SELECT 
        ERROR_NUMBER() AS ErrorNumber,
        ERROR_MESSAGE() AS ErrorMessage;
END CATCH;
GO

결과 집합은 다음과 같습니다.

ErrorNumber ErrorMessage
----------- ---------------------------------------
208         Invalid object name 'NonExistentTable'.

자세한 내용은 지연된 이름 확인 및 컴파일실행 계획 캐싱 및 다시 사용의 "실행 계획 다시 컴파일"을 참조하십시오.

커밋할 수 없는 트랜잭션

TRY...CATCH 구문 내에서 트랜잭션은 계속 열려 있지만 커밋할 수 없는 상태가 될 수 있습니다. 트랜잭션은 데이터 수정 또는 저장점으로의 롤백 시도와 같이 트랜잭션 로그에 쓰기를 생성하는 작업을 수행할 수 없습니다. 그러나 이 상태에서 트랜잭션이 확보한 잠금은 유지 관리되고 연결도 계속 열려 있습니다. 트랜잭션의 결과는 ROLLBACK 문이 실행되거나 일괄 처리가 끝나고 데이터베이스 엔진에서 자동으로 트랜잭션이 롤백된 후에야 되돌려집니다. 트랜잭션이 커밋할 수 없는 상태가 되었을 때 오류 메시지가 전송되지 않은 경우 일괄 처리가 완료되면 커밋할 수 없는 트랜잭션이 검색되어 롤백되었음을 나타내는 오류 메시지가 클라이언트 응용 프로그램으로 전송됩니다.

오류가 발생할 경우 트랜잭션이 TRY 블록 내에서 커밋할 수 없는 상태가 됩니다. 이 오류가 발생하지 않으면 트랜잭션이 종료됩니다. 예를 들어 CREATE TABLE과 같은 DDL(데이터 정의 언어) 문에서 발생하는 대부분의 오류와 SET XACT_ABORT가 ON으로 설정되어 있을 때 발생하는 대부분의 오류는 TRY 블록 외부에서는 트랜잭션을 종료하지만 TRY 블록 내에서는 트랜잭션을 커밋할 수 없게 만듭니다.

CATCH 블록의 코드에서는 XACT_STATE 함수를 사용하여 트랜잭션 상태를 테스트해야 합니다. XACT_STATE는 세션에 커밋할 수 없는 트랜잭션이 있을 경우 -1을 반환합니다. XACT_STATE가 -1을 반환하는 경우 CATCH 블록은 로그에 쓰기를 생성하는 작업을 수행하지 않아야 합니다. 다음 코드 예에서는 DDL 문에서 오류를 생성하고 가장 적절한 조치를 취하기 위해 XACT_STATE를 사용하여 트랜잭션 상태를 테스트합니다.

USE AdventureWorks;
GO

-- Verify that the table does not exist.
IF OBJECT_ID (N'my_books', N'U') IS NOT NULL
    DROP TABLE my_books;
GO

-- Create table my_books.
CREATE TABLE my_books
    (
    Isbn        int PRIMARY KEY,
    Title       NVARCHAR(100)
    );
GO

BEGIN TRY
    BEGIN TRANSACTION;
        -- This statement will generate an error because the 
        -- column author does not exist in the table.
        ALTER TABLE my_books
            DROP COLUMN author;
    -- If the DDL statement succeeds, commit the transaction.
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    SELECT
        ERROR_NUMBER() as ErrorNumber,
        ERROR_MESSAGE() as ErrorMessage;

    -- Test XACT_STATE for 1 or -1.
    -- 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
            N'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
            N'The transaction is committable. ' +
            'Committing transaction.'
        COMMIT TRANSACTION;   
    END;
END CATCH;
GO

교착 상태 처리

TRY...CATCH를 사용하여 교착 상태를 처리할 수 있습니다. CATCH 블록에서 1205 교착 상태 오류를 catch할 수 있으며 스레드의 잠금이 해제될 때까지 트랜잭션을 롤백할 수 있습니다. 교착 상태에 대한 자세한 내용은 교착 상태를 참조하십시오.

다음 예에서는 TRY...CATCH를 사용하여 교착 상태를 처리하는 방법을 보여 줍니다. 이 첫 번째 섹션에서는 교착 상태를 보여 주는 데 사용할 테이블과 오류 정보를 인쇄하는 데 사용할 저장 프로시저를 만듭니다.

USE AdventureWorks;
GO

-- Verify that the table does not exist.
IF OBJECT_ID (N'my_sales',N'U') IS NOT NULL
    DROP TABLE my_sales;
GO

-- Create and populate the table for deadlock simulation.
CREATE TABLE my_sales 
    (
    Itemid       INT PRIMARY KEY,
    Sales        INT not null
    );
GO

INSERT my_sales (itemid, sales) VALUES (1, 1);
INSERT my_sales (itemid, sales) VALUES (2, 1);
GO
  
-- Verify that the stored procedure for error printing
-- does not exist.
IF OBJECT_ID (N'usp_MyErrorLog',N'P') IS NOT NULL
    DROP PROCEDURE usp_MyErrorLog;
GO

-- Create a stored procedure for printing error information.
CREATE PROCEDURE usp_MyErrorLog
AS
    PRINT 
        'Error ' + CONVERT(VARCHAR(50), ERROR_NUMBER()) +
        ', Severity ' + CONVERT(VARCHAR(5), ERROR_SEVERITY()) +
        ', State ' + CONVERT(VARCHAR(5), ERROR_STATE()) + 
        ', Line ' + CONVERT(VARCHAR(5), ERROR_LINE());
    PRINT 
        ERROR_MESSAGE();
GO

세션 1과 세션 2의 다음 코드 스크립트는 별개의 두 SQL Server Management Studio 연결에서 동시에 실행됩니다. 두 세션 모두 테이블의 같은 행을 업데이트하려고 합니다. 한 세션은 첫 번째 시도에서 업데이트 작업을 성공하고 다른 세션은 교착 상태가 됩니다. 교착 상태 오류로 인해 실행은 CATCH 블록으로 넘어가고 트랜잭션은 커밋할 수 없는 상태가 됩니다. CATCH 블록 내에서 교착 상태가 된 세션은 트랜잭션을 롤백하고 업데이트가 성공하거나 다시 시도 제한에 도달할 때까지 테이블 업데이트를 다시 시도할 수 있습니다.

세션 1 세션 2
USE AdventureWorks;
GO
-- Declare and set variable
-- to track number of retries
-- to try before exiting.
DECLARE @retry INT;
SET @retry = 5;
-- Keep trying to update 
-- table if this task is 
-- selected as the deadlock 
-- victim.
WHILE (@retry > 0)
BEGIN
    BEGIN TRY
        BEGIN TRANSACTION;
    
        UPDATE my_sales
        SET sales = sales + 1
        WHERE itemid = 1;
        WAITFOR DELAY '00:00:13';
    
        UPDATE my_sales
        SET sales = sales + 1
        WHERE itemid = 2;
        SET @retry = 0;
        COMMIT TRANSACTION;
    END TRY
    BEGIN CATCH 
        -- Check error number.
        -- If deadlock victim error,
        -- then reduce retry count
        -- for next update retry. 
        -- If some other error
        -- occurred, then exit
        -- retry WHILE loop.
        IF (ERROR_NUMBER() = 1205)
            SET @retry = @retry - 1;
        ELSE
            SET @retry = -1;
        -- Print error information.
        EXECUTE usp_MyErrorLog;
  
        IF XACT_STATE() <> 0
            ROLLBACK TRANSACTION;
    END CATCH;
END; -- End WHILE loop.
GO
USE AdventureWorks;
GO
-- Declare and set variable
-- to track number of retries
-- to try before exiting.
DECLARE @retry INT;
SET @retry = 5;
--Keep trying to update 
-- table if this task is 
-- selected as the deadlock 
-- victim.
WHILE (@retry > 0)
BEGIN
    BEGIN TRY
       BEGIN TRANSACTION;
    
        UPDATE my_sales
        SET sales = sales + 1
        WHERE itemid = 2;
        WAITFOR DELAY '00:00:07';
    
        UPDATE my_sales
        SET sales = sales + 1
        WHERE itemid = 1;
        SET @retry = 0;
        COMMIT TRANSACTION;
    END TRY
    BEGIN CATCH 
        -- Check error number.
        -- If deadlock victim error,
        -- then reduce retry count
        -- for next update retry. 
        -- If some other error
        -- occurred, then exit
        -- retry WHILE loop.
        IF (ERROR_NUMBER() = 1205)
            SET @retry = @retry - 1;
        ELSE
            SET @retry = -1;
        -- Print error information.
        EXECUTE usp_MyErrorLog;
  
        IF XACT_STATE() <> 0
            ROLLBACK TRANSACTION;
    END CATCH;
END; -- End WHILE loop.
GO

RAISERROR를 사용하는 TRY...CATCH

TRY...CATCH 구문의 TRY 또는 CATCH 블록에서 RAISERROR를 사용하여 오류 처리 동작에 영향을 줄 수 있습니다.

TRY 블록 내에서 심각도 11-19의 RAISERROR를 실행하면 연결된 CATCH 블록으로 제어가 넘어갑니다. CATCH 블록 내에서 심각도 11-19의 RAISERROR를 실행하면 호출 응용 프로그램이나 일괄 처리에 오류가 반환됩니다. 따라서 RAISERROR를 사용하여 호출자에게 CATCH 블록을 실행시킨 오류에 대한 정보를 반환할 수 있습니다. 원래 오류 번호를 포함하여 RAISERROR 메시지에서 TRY...CATCH 오류 함수가 제공한 오류 정보를 캡처할 수 있지만 RAISERROR에 대한 오류 번호는 >= 50000이어야 합니다.

심각도 10 이하의 RAISERROR를 실행하면 CATCH 블록을 호출하지 않고 호출 응용 프로그램이나 일괄 처리에 정보 메시지가 반환됩니다.

심각도 20 이상의 RAISERROR를 실행하면 CATCH 블록을 호출하지 않고 데이터베이스 연결이 닫힙니다.

다음 코드 예에서는 CATCH 블록 내에서 RAISERROR를 사용하여 호출 응용 프로그램이나 일괄 처리에 원래 오류 정보를 반환하는 방법을 보여 줍니다. usp_GenerateError 저장 프로시저는 TRY 블록 내에서 제약 조건 위반 오류를 생성하는 DELETE 문을 실행합니다. 오류가 생성되면 usp_GenerateError 내의 연결된 CATCH 블록으로 실행이 넘어간 다음 usp_RethrowError 저장 프로시저가 실행되어 RAISERROR를 통해 제약 조건 위반 오류 정보를 발생시킵니다. RAISERROR를 통해 생성된 이 오류는 usp_GenerateError를 실행한 호출 일괄 처리에 반환되고 호출 일괄 처리 내의 연결된 CATCH 블록으로 실행이 넘어갑니다.

[!참고] RAISERROR는 1에서 127까지 상태의 오류만 생성할 수 있습니다. 데이터베이스 엔진은 상태 0의 오류를 발생시킬 수 있으므로 RAISERROR의 상태 매개 변수 값으로 전달하기 전에 ERROR_STATE에서 반환된 오류 상태를 확인하는 것이 좋습니다.

USE AdventureWorks;
GO

-- Verify that stored procedure does not exist.
IF OBJECT_ID (N'usp_RethrowError',N'P') IS NOT NULL
    DROP PROCEDURE usp_RethrowError;
GO

-- Create the stored procedure to generate an error using 
-- RAISERROR. The original error information is used to
-- construct the msg_str for RAISERROR.
CREATE PROCEDURE usp_RethrowError AS
    -- Return if there is no error information to retrieve.
    IF ERROR_NUMBER() IS NULL
        RETURN;

    DECLARE 
        @ErrorMessage    NVARCHAR(4000),
        @ErrorNumber     INT,
        @ErrorSeverity   INT,
        @ErrorState      INT,
        @ErrorLine       INT,
        @ErrorProcedure  NVARCHAR(200);

    -- Assign variables to error-handling functions that 
    -- capture information for RAISERROR.
    SELECT 
        @ErrorNumber = ERROR_NUMBER(),
        @ErrorSeverity = ERROR_SEVERITY(),
        @ErrorState = ERROR_STATE(),
        @ErrorLine = ERROR_LINE(),
        @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');

    -- Build the message string that will contain original
    -- error information.
    SELECT @ErrorMessage = 
        N'Error %d, Level %d, State %d, Procedure %s, Line %d, ' + 
            'Message: '+ ERROR_MESSAGE();

    -- Raise an error: msg_str parameter of RAISERROR will contain
    -- the original error information.
    RAISERROR 
        (
        @ErrorMessage, 
        @ErrorSeverity, 
        1,               
        @ErrorNumber,    -- parameter: original error number.
        @ErrorSeverity,  -- parameter: original error severity.
        @ErrorState,     -- parameter: original error state.
        @ErrorProcedure, -- parameter: original error procedure name.
        @ErrorLine       -- parameter: original error line number.
        );
GO

-- Verify that stored procedure does not exist.
IF OBJECT_ID (N'usp_GenerateError',N'P') IS NOT NULL
    DROP PROCEDURE usp_GenerateError;
GO

-- Create a stored procedure that generates a constraint violation
-- error. The error is caught by the CATCH block where it is 
-- raised again by executing usp_RethrowError.
CREATE PROCEDURE usp_GenerateError 
AS 
    BEGIN TRY
        -- A FOREIGN KEY constraint exists on the table. This 
        -- statement will generate a constraint violation error.
        DELETE FROM Production.Product
            WHERE ProductID = 980;
    END TRY
    BEGIN CATCH
        -- Call the procedure to raise the original error.
        EXEC usp_RethrowError;
    END CATCH;
GO

-- In the following batch, an error occurs inside 
-- usp_GenerateError that invokes the CATCH block in
-- usp_GenerateError. RAISERROR inside this CATCH block
-- generates an error that invokes the outer CATCH
-- block in the calling batch.
BEGIN TRY  -- outer TRY
    -- Call the procedure to generate an error.
    EXECUTE usp_GenerateError;
END TRY
BEGIN CATCH  -- Outer CATCH
    SELECT
        ERROR_NUMBER() as ErrorNumber,
        ERROR_MESSAGE() as ErrorMessage;
END CATCH;
GO

실행 흐름 변경

TRY 블록이나 CATCH 블록 내에서 실행 흐름을 변경하려면 GOTO를 사용할 수 있습니다. GOTO를 사용하면 TRY 블록이나 CATCH 블록을 종료할 수도 있지만 TRY 블록이나 CATCH 블록을 시작할 수는 없습니다.

AdventureWorks 예제 데이터베이스의 오류 처리 솔루션

AdventureWorks 예제 데이터베이스에는 TRY...CATCH 구문의 CATCH 블록에서 catch한 오류에 대한 정보를 기록하도록 디자인된 오류 처리 솔루션이 있어 나중에 오류를 쿼리하거나 분석할 수 있습니다.

dbo.ErrorLog 테이블

ErrorLog 테이블에는 오류 번호, 오류 심각도, 오류 상태, 오류가 발생한 저장 프로시저 또는 트리거 이름, 오류가 발생한 줄 번호, 오류 메시지의 전체 텍스트에 대한 정보가 기록되고 오류가 발생한 날짜와 시간, 오류 생성 루틴을 실행한 사용자 이름도 기록됩니다. 이 테이블은 TRY...CATCH 구문의 CATCH 블록 범위에서 저장 프로시저 uspLogError가 실행될 때 채워집니다. 자세한 내용은 ErrorLog 테이블(AdventureWorks)을 참조하십시오.

dbo.uspLogError

uspLogError 저장 프로시저는 TRY...CATCH 구문의 CATCH 블록으로 실행이 넘어가게 한 오류에 대한 정보를 ErrorLog 테이블에 기록합니다. uspLogErrorErrorLog 테이블에 오류 정보를 삽입하려면 다음 조건이 있어야 합니다.

  • uspLogError가 CATCH 블록 범위 내에서 실행됩니다.
  • 현재 트랜잭션이 커밋할 수 없는 상태인 경우 uspLogError를 실행하기 전에 트랜잭션이 롤백됩니다.

uspLogError의 출력 매개 변수 @ErrorLogIDuspLogErrorErrorLog 테이블에 삽입한 행의 ErrorLogID를 반환합니다. @ErrorLogID의 기본값은 0입니다. 다음 예에서는 uspLogError에 대한 코드를 보여 줍니다. 자세한 내용은 AdventureWorks에 있는 저장 프로시저를 참조하십시오.

CREATE PROCEDURE [dbo].[uspLogError] 
    @ErrorLogID [int] = 0 OUTPUT  -- Contains the ErrorLogID of the row inserted
                                  -- by uspLogError in the ErrorLog table.

AS
BEGIN
    SET NOCOUNT ON;

    -- Output parameter value of 0 indicates that error 
    -- information was not logged.
    SET @ErrorLogID = 0;

    BEGIN TRY
        -- Return if there is no error information to log.
        IF ERROR_NUMBER() IS NULL
            RETURN;

        -- Return if inside an uncommittable transaction.
        -- Data insertion/modification is not allowed when 
        -- a transaction is in an uncommittable state.
        IF XACT_STATE() = -1
        BEGIN
            PRINT 'Cannot log error since the current transaction is in an uncommittable state. ' 
                + 'Rollback the transaction before executing uspLogError in order to successfully log error information.';
            RETURN;
        END;

        INSERT [dbo].[ErrorLog] 
            (
            [UserName], 
            [ErrorNumber], 
            [ErrorSeverity], 
            [ErrorState], 
            [ErrorProcedure], 
            [ErrorLine], 
            [ErrorMessage]
            ) 
        VALUES 
            (
            CONVERT(sysname, CURRENT_USER), 
            ERROR_NUMBER(),
            ERROR_SEVERITY(),
            ERROR_STATE(),
            ERROR_PROCEDURE(),
            ERROR_LINE(),
            ERROR_MESSAGE()
            );

        -- Pass back the ErrorLogID of the row inserted
        SELECT @ErrorLogID = @@IDENTITY;
    END TRY
    BEGIN CATCH
        PRINT 'An error occurred in stored procedure uspLogError: ';
        EXECUTE [dbo].[uspPrintError];
        RETURN -1;
    END CATCH
END; 

dbo.uspPrintError

uspPrintError 저장 프로시저는 TRY...CATCH 구문의 CATCH 블록으로 실행이 넘어가게 한 오류에 대한 정보를 인쇄합니다. CATCH 블록 범위에서 uspPrintError를 실행해야 합니다. 그렇지 않으면 아무 오류 정보도 인쇄하지 않은 채 반환됩니다. 다음 예에서는 uspPrintError에 대한 코드를 보여 줍니다. 자세한 내용은 AdventureWorks에 있는 저장 프로시저를 참조하십시오.

CREATE PROCEDURE [dbo].[uspPrintError] 
AS
BEGIN
    SET NOCOUNT ON;

    -- Print error information. 
    PRINT 'Error ' + CONVERT(varchar(50), ERROR_NUMBER()) +
          ', Severity ' + CONVERT(varchar(5), ERROR_SEVERITY()) +
          ', State ' + CONVERT(varchar(5), ERROR_STATE()) + 
          ', Procedure ' + ISNULL(ERROR_PROCEDURE(), '-') + 
          ', Line ' + CONVERT(varchar(5), ERROR_LINE());
    PRINT ERROR_MESSAGE();
END;

오류 처리 예

다음 예에서는 AdventureWorks 오류 처리 솔루션을 보여 줍니다. TRY 블록 내의 코드는 Production.Product 테이블에서 ProductID 980의 레코드를 삭제하려고 합니다. 테이블의 FOREIGN KEY 제약 조건 때문에 DELETE 문이 성공하지 못하게 되고 제약 조건 위반 오류가 생성됩니다. 이 오류로 인해 CATCH 블록으로 실행이 넘어갑니다. CATCH 블록 내에서 다음 작업이 발생합니다.

  • uspPrintError가 오류 정보를 인쇄합니다.
  • 트랜잭션이 롤백되면 uspLogErrorErrorLog 테이블에 오류 정보를 입력하고 삽입된 행의 ErrorLogID@ErrorLogID OUTPUT 매개 변수로 반환합니다.
USE AdventureWorks;
GO

-- Variable to store ErrorLogID value of the row
-- inserted in the ErrorLog table by uspLogError 
DECLARE @ErrorLogID INT;

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.
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    -- Call procedure to print error information.
    EXECUTE dbo.uspPrintError;

    -- Roll back any active or uncommittable transactions before
    -- inserting information in the ErrorLog.
    IF XACT_STATE() <> 0
    BEGIN
        ROLLBACK TRANSACTION;
    END

    EXECUTE dbo.uspLogError @ErrorLogID = @ErrorLogID OUTPUT;
END CATCH; 

-- Retrieve logged error information.
SELECT * FROM dbo.ErrorLog WHERE ErrorLogID = @ErrorLogID;
GO

중첩 오류 처리 예

다음 예에서는 중첩 TRY...CATCH 구문 사용을 보여 줍니다.

BEGIN TRY
    BEGIN TRY
        SELECT CAST('invalid_date' AS datetime)
    END TRY
    BEGIN CATCH 
        PRINT 'Inner TRY error number: ' +    
            CONVERT(varchar,ERROR_NUMBER()) + ' on line: ' +
            CONVERT(varchar, ERROR_LINE())
    END CATCH
    SELECT CAST('invalid_int' AS int)
END TRY
BEGIN CATCH
    PRINT 'Outer TRY error mumber: ' + CONVERT(varchar,ERROR_NUMBER())+ 
            ' on line: ' + CONVERT(varchar, ERROR_LINE())
END CATCH

결과 집합은 다음과 같습니다.

Inner TRY error number: 241 on line: 3

Outer TRY error number: 245 on line: 9

참고 항목

개념

응용 프로그램 오류 및 메시지 처리
PRINT 사용
@@ERROR 사용
RAISERROR 사용
트랜잭션(데이터베이스 엔진)
교착 상태

관련 자료

ERROR_LINE(Transact-SQL)
ERROR_MESSAGE(Transact-SQL)
ERROR_NUMBER(Transact-SQL)
ERROR_PROCEDURE(Transact-SQL)
ERROR_SEVERITY(Transact-SQL)
ERROR_STATE(Transact-SQL)
XACT_STATE(Transact-SQL)
RAISERROR(Transact-SQL)
TRY...CATCH(Transact-SQL)
sys.messages(Transact-SQL)
SET XACT_ABORT(Transact-SQL)
GOTO(Transact-SQL)
BEGIN...END(Transact-SQL)
데이터베이스 엔진 오류 심각도
AdventureWorks 데이터 사전

도움말 및 정보

SQL Server 2005 지원 받기

변경 내역

릴리스 내역

2006년 7월 17일

새로운 내용
  • 중첩 오류 처리 예를 추가했습니다.

2006년 4월 14일

새로운 내용
  • 커밋할 수 없는 트랜잭션의 동작 변경에 대한 정보를 추가했습니다.