SQL TRY错误没有捕获

时间:2018-02-22 17:27:54

标签: sql-server tsql error-handling

拥有存储过程并以

格式包装代码
BEGIN TRY
    BEGIN TRAN
        ...code
    COMMIT
END TRY

BEGIN CATCH
    ROLLBACK
    ...code
END CATCH

在我开始对各种错误进行一些测试以确保它们正确地输入我的日志表之前,哪个工作正常。

但是,当我运行它时,它会失败,并且不会回滚并需要手动回滚。这就像代码没有意识到它处于TRY块中。

有什么想法吗? (下面的代码,希望它可以在其他人的系统上重新创建,而不是如何配置我所在系统的奇怪方式)

BEGIN
     SET NOCOUNT ON

    BEGIN TRY

        BEGIN TRAN

            --------------------------------------------------------------------- 
            -- Create test table
            ---------------------------------------------------------------------
            IF OBJECT_ID('tempdb..#DateOfBirth') IS NOT NULL DROP TABLE #DateOfBirth
            CREATE TABLE #DateOfBirth
            (
                DateOfBirth DATE
            )
            INSERT INTO #DateOfBirth
            VALUES
                ('1984-12-09')
                ,('1977-12-09')
                ,('2015-03-12')
                ,('1967-01-15')

            --------------------------------------------------------------------- 
            -- Date Of Birth
                -- This Insert errors
            ---------------------------------------------------------------------
            IF OBJECT_ID('tempdb..#DOB') IS NOT NULL DROP TABLE #DOB
            CREATE TABLE #DOB
            (
                groupID INT IDENTITY(1,1)
                , DateOfBirth INT -- Data Type mismatch
            )

            INSERT INTO #DOB
            SELECT DateOfBirth
            FROM #DateOfBirth

        COMMIT
    END TRY

    BEGIN CATCH

        PRINT 'Rollback'

        ROLLBACK

        DECLARE @ErrorMessage   NVARCHAR(4000) = ERROR_MESSAGE(),
                @ErrorState     INT = ERROR_STATE(),
                @ErrorSeverity  INT = ERROR_SEVERITY();

        RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState);

    END CATCH

END
GO

2 个答案:

答案 0 :(得分:2)

无法捕获CATCH块相同范围内的编译错误。也就是说,在每个语句之前添加一个PRINT:

PRINT 'BATCH STARTED';
BEGIN
    SET NOCOUNT ON

    BEGIN TRY

        PRINT 'BEGIN TRAN';
        BEGIN TRAN;

        IF OBJECT_ID('tempdb..#DateOfBirth') IS NOT NULL DROP TABLE #DateOfBirth;

        PRINT 'CREATING #DateOfBirth';
        CREATE TABLE #DateOfBirth
        (
            DateOfBirth DATE
        );

        PRINT 'INSERTING INTO #DateOfBirth';
        INSERT INTO #DateOfBirth
        VALUES
            ('1984-12-09')
            ,('1977-12-09')
            ,('2015-03-12')
            ,('1967-01-15')

        IF OBJECT_ID('tempdb..#DOB') IS NOT NULL DROP TABLE #DOB;

        PRINT 'CREATING #DOB';
        CREATE TABLE #DOB
        (
            groupID INT IDENTITY(1,1)
            , DateOfBirth INT -- Data Type mismatch
        );

        PRINT 'INSERTING INTO #DOB';
        INSERT INTO #DOB
        SELECT DateOfBirth
        FROM #DateOfBirth;

        PRINT 'COMMIT';
        COMMIT;
    END TRY

    BEGIN CATCH

        PRINT 'ROLLBACK';

        ROLLBACK;

        DECLARE @ErrorMessage   NVARCHAR(4000) = ERROR_MESSAGE(),
                @ErrorState     INT = ERROR_STATE(),
                @ErrorSeverity  INT = ERROR_SEVERITY();

        RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState);

    END CATCH;

END;
GO

当最初在新会话上执行此批处理时,生成的消息为:

BATCH STARTED
BEGIN TRAN
CREATING #DateOfBirth
INSERTING INTO #DateOfBirth
CREATING #DOB
INSERTING INTO #DOB
Msg 206, Level 16, State 2, Line 36
Operand type clash: date is incompatible with int

重要的一点是,在语句编译期间发生此错误,而不是执行。因为在编译批处理时临时表不存在,所以引用这些表的语句的编译将推迟到语句运行之前。编译期间发生的任何错误都不能被同一范围内的CATCH块捕获。

如果使用动态SQL执行语句,则可以捕获编译错误,以便编译在不同的范围内进行:

PRINT 'BATCH STARTED';
BEGIN
    SET NOCOUNT ON

    BEGIN TRY

        PRINT 'BEGIN TRAN';
        BEGIN TRAN;

        EXECUTE('
        IF OBJECT_ID(''tempdb..#DateOfBirth'') IS NOT NULL DROP TABLE #DateOfBirth;

        PRINT ''CREATING #DateOfBirth'';
        CREATE TABLE #DateOfBirth
        (
            DateOfBirth DATE
        );

        PRINT ''INSERTING INTO #DateOfBirth'';
        INSERT INTO #DateOfBirth
        VALUES
            (''1984-12-09'')
            ,(''1977-12-09'')
            ,(''2015-03-12'')
            ,(''1967-01-15'')

        IF OBJECT_ID(''tempdb..#DOB'') IS NOT NULL DROP TABLE #DOB;

        PRINT ''CREATING #DOB'';
        CREATE TABLE #DOB
        (
            groupID INT IDENTITY(1,1)
            , DateOfBirth INT -- Data Type mismatch
        );

        PRINT ''INSERTING INTO #DOB'';
        INSERT INTO #DOB
        SELECT DateOfBirth
        FROM #DateOfBirth;

        PRINT ''COMMIT'';
        COMMIT;
        ');


    END TRY

    BEGIN CATCH

        PRINT 'ROLLBACK';

        ROLLBACK;

        DECLARE @ErrorMessage   NVARCHAR(4000) = ERROR_MESSAGE(),
                @ErrorState     INT = ERROR_STATE(),
                @ErrorSeverity  INT = ERROR_SEVERITY();

        RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState);

    END CATCH;

END;
GO

在这种情况下,输入CATCH块并回滚事务:

BATCH STARTED
BEGIN TRAN
CREATING #DateOfBirth
INSERTING INTO #DateOfBirth
CREATING #DOB
INSERTING INTO #DOB
ROLLBACK
Msg 50000, Level 16, State 2, Line 58
Operand type clash: date is incompatible with int

顺便说一下,我强烈建议您指定' SET XACT_ABORT ON'帮助确保在未执行CATCH块的情况下发生错误后回滚事务(例如客户端查询超时错误)。

答案 1 :(得分:1)

您遇到的错误是:

Msg 206, Level 16, State 2, Line 39
Operand type clash: date is incompatible with int

对于这种类型的错误,建议在应用程序范围内处理,因为批处理被简单地中止。

我编写了一些资源:

希望这会对你有所帮助。