【问题标题】:Try/Catch not work on T-SQL stored procedure (not rollback transaction)Try/Catch 不适用于 T-SQL 存储过程(不是回滚事务)
【发布时间】:2014-10-25 10:18:38
【问题描述】:

我有这个存储过程:

CREATE PROCEDURE [dbo].[MYSTOREDPROCEDURENAME]
AS
BEGIN   

BEGIN TRANSACTION;

BEGIN TRY

    CREATE TABLE [dbo].[#Temp](
        Col1 varchar(50) NOT NULL,
        Col2 varchar(50) NOT NULL,
        Col3 varchar(50) NOT NULL,
        Col4 smallint NULL)

    INSERT INTO [dbo].[#Temp]([Col1], [Col2], [Col3], [Col4])
    SELECT [Col1],[Col2],[Col3],[Col4] FROM [dbo].[MyTable]

    TRUNCATE TABLE [dbo].[MyTable]

    INSERT INTO [dbo].[MyTable] ( Col1, Col2, Col3, Col4 )
    SELECT Field1, Field2, Field3, ISNULL((SELECT Col4 FROM [dbo].[#Temp] WHERE [dbo].[#Temp].[Col1]=MyTable2.Field1 AND [dbo].[#Temp].[Col2]=MyTable2.Field2 AND [dbo].[#Temp].[Col3]=MyTable2.Field3),1) AS Espr1
    FROM [dbo].[MyTable2]

    DROP TABLE [dbo].[#Temp]
END TRY
BEGIN CATCH
    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;

    IF @@TRANCOUNT > 0
        ROLLBACK TRANSACTION;
END CATCH;

IF @@TRANCOUNT > 0
    COMMIT TRANSACTION;
END

如果我执行这个sp,就会显示这个错误信息:

消息 468,级别 16,状态 9,过程 MYSTOREDPROCEDURENAME,第 32 行 无法解决等于操作中“Latin1_General_CI_AS”和“SQL_Latin1_General_CP1_CI_AS”之间的排序规则冲突。 消息 266,级别 16,状态 2,过程 MYSTOREDPROCEDURENAME,第 32 行 EXECUTE 之后的事务计数表明缺少 COMMIT 或 ROLLBACK TRANSACTION 语句。先前计数 = 0,当前计数 = 1。

现在,除了错误之外,我不明白为什么没有达到块“catch”并且没有完成事务回滚(在错误“MyTable”被锁定之后!)

我哪里错了?为什么“catch”块不起作用?

附:存储过程是在 Sql Server 2005 上执行的

【问题讨论】:

    标签: sql-server tsql stored-procedures transactions try-catch


    【解决方案1】:

    我不知道为什么,但是 try/catch 只适用于内部开始的事务,而不是 try 部分之外的事务。所以你可以将begincommit 都移动到try 部分,catch 部分仍然有rollback

    此外,try/catch 无法捕获某些错误,例如延迟名称解析错误。这是设计使然。

    UPD:啊,我明白了。这是答案:Issues with T-SQL TRY CATCH?

    看起来,SQL Server 认为排序规则冲突与延迟名称解析失败一样严重。此特定错误无法被发生该错误的同一模块中的 try/catch 捕获。

    附:我最后一次遇到这个错误是在 MSSQL 2000 上,其中还没有引入 try/catch,所以我不知道它没有被处理。抱歉误导了。

    【讨论】:

    【解决方案2】:

    您尝试阻止未达到的原因是因为在执行任何代码之前很久,它就已被解析和验证。这失败了,所以什么都不执行。

    您的代码中有 2 个错误导致其无法运行,第一个是排序规则错误。您真的应该更彻底地研究在 1 个数据库中发生这种情况并解决根本原因,但现在您可以在有问题的列上使用 COLLATE Latin1_General_CI_AS。

    第二个错误正是 Ennor 所说的,你周围有一个事务 try/catch。这不是它的工作原理。例如,您可以简单地完全删除 BEGIN/COMMIT,因为它们实际上并没有做任何事情。

    这是一个功能齐全的示例,我刚刚在通用 sql server 数据库中运行,默认选项名为 [demo]。

    use demo
    go
    
    create table [MyTable] (
        Col1 varchar(50) not null
        , Col2 varchar(50) not null
        , Col3 varchar(50) not null
        , Col4 smallint null
        )
    
    insert MyTable
    values (
        'some string'
        , 'some string'
        , 'some string'
        , 1
        )
    
    create table [MyTable2] (
        Col1 varchar(50) not null
        , Col2 varchar(50) not null
        , Col3 varchar(50) not null
        , Col4 smallint null
        )
    go
    
    create procedure [dbo].[my_proc]
    as
    begin
    begin try
    begin transaction
        create table #Temp (
            Col1 varchar(50) not null
            , Col2 varchar(50) not null
            , Col3 varchar(50) not null
            , Col4 smallint null
            )
    
        insert into #Temp (
            [Col1]
            , [Col2]
            , [Col3]
            , [Col4]
            )
        select [Col1]
            , [Col2]
            , [Col3]
            , [Col4]
        from [dbo].[MyTable]
    
        truncate table [dbo].[MyTable]
    
        insert into [dbo].[MyTable] (
            Col1
            , Col2
            , Col3
            , Col4
            )
        select [Col1]
            , [Col2]
            , [Col3]
            , ISNULL((
                    select Col4
                    from #Temp
                    where #Temp.[Col1] = MyTable2.[Col1]
                        and #Temp.[Col2] = MyTable2.[Col2]
                        and #Temp.[Col3] = MyTable2.[Col3]
                    ), 1) as Espr1
        from [dbo].[MyTable2]
    
        drop table #Temp
        if @@TRANCOUNT <> 0
        commit transaction
    end try
    
        begin catch
            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;
    
            if @@TRANCOUNT > 0
                rollback transaction;
        end catch;
    end
    
    exec my_proc
    

    【讨论】:

    • 我试图在“开始尝试”之前删除“开始事务”(如示例中所示),但这样,如果出现错误,MyTable 为空(因为截断)而它不应该
    • @LukePet 在这种情况下,您必须更全面地解释您希望代码做什么。
    • 我只是希望“开始尝试”块中包含的所有指令都作为“原子”(即事务)执行,并且在出现错误时返回其初始状态。跨度>
    • 我将事务添加到 try 中,现在可以按照您的描述工作,并且仅在出现其他错误时才抛出捕获。
    猜你喜欢
    • 2022-01-06
    • 2015-03-22
    • 2020-07-08
    • 2012-05-14
    • 1970-01-01
    • 1970-01-01
    • 2015-05-17
    • 1970-01-01
    • 2017-09-22
    相关资源
    最近更新 更多