交易是否需要尝试捕获?

时间:2016-05-06 14:51:37

标签: sql-server tsql

我是一名c#开发人员,学习更多TSQL。我写了一个这样的剧本:

begin transaction
--Insert into several tables
end transaction

但我被告知这不是一个好主意并使用这样的东西:

BEGIN TRANSACTION;

BEGIN TRY
    -- Generate a constraint violation error.
    DELETE FROM Production.Product
    WHERE ProductID = 980;
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;
GO

我不明白为什么第二个例子更正确。第一个不会以同样的方式工作吗?似乎第一个要么更新所有表,要么根本不更新?我不明白为什么在提交之前检查@@TRANCOUNT是必要的。

3 个答案:

答案 0 :(得分:4)

只有在您进入try块并且在实际语句之前才打开一个事务,并且直接提交它,不要等待您的控制转到批处理结束以提交您的事务。

一旦你进入Try Block并且你已经打开了一个事务,如果出现问题控件将跳转到CATCH块,只需在那里回滚你的事务并根据需要进行其他错误处理。

在使用@@ ROWCOUNT函数实际回滚事务检查任何打开的事务之前,我添加了一点检查,它在这种情况下确实没有多大意义。在打开事务之前在try块中进行一些验证检查更有用,例如检查参数值和其他内容,如果任何验证检查失败,则在try块中引发错误,在这种情况下,控制将跳转到catch块甚至没有在那里打开交易,你可以检查任何打开的交易和回滚,如果有任何打开的交易。在你的情况下,你真的不需要检查任何打开的交易,因为你不会进入catch块,除非你的交易中出现问题。

BEGIN TRY

  BEGIN TRANSACTION 
     -- Multiple Inserts
    INSERT INTO....
    INSERT INTO.... 
    INSERT INTO.... 
 COMMIT TRANSACTION 
    PRINT 'Rows inserted successfully...'

END TRY

BEGIN CATCH 
  IF (@@TRANCOUNT > 0)
   BEGIN
      ROLLBACK TRANSACTION 
      PRINT 'Error detected, all changes reversed'
   END 
    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
END CATCH

答案 1 :(得分:1)

我想以C#开发人员的身份提出我的观点:

在上面给出的简单场景中(只是从脚本插入几个表),没有理由添加try / catch,因为它不会给事务带来任何好处。这两个示例都会产生完全相同的结果:要么插入所有表,要么不插入。数据库的状态保持一致。 (由于从不调用COMMIT TRANSACTION,因此在脚本结束时Sql Server将隐式调用回滚。)

但是,有时您可以在try / catch中执行集成错误处理无法实现的操作。例如,将错误记录到错误表中。

在我的C#体验中,唯一一次使用Try / Catch是指在开发人员控件之外的事情,例如尝试打开文件。在这种情况下,管理.Net框架生成的异常的唯一方法是通过Try / Catch。

如果我正在执行存储过程,并且想要手动检查数据状态并手动调用ROLLBACK TRANSACTION,我可以看到。但它仍然不需要尝试/捕获。

答案 2 :(得分:0)

我对T-SQL中的TRY ... CATCH有两种想法。

虽然它是该语言的一个潜在有用的补充,但它可用的事实并不总是使用它的理由。

带上你的

DELETE FROM Table WHERE...

示例(我意识到这只是一个例子)。失败的唯一方法就是如果代码严重脱离了架构。 (例如,如果有人在PK端创建了一个带有Table的外键)。

如果进行了适当的测试,代码和架构之间的这种不匹配应该永远不会投入生产。假设它确实如此,恕我直言的“粗鲁”,无中介错误消息将更好地指示出错是什么,而不是将其“礼貌”包装到SELECT语句中以返回到客户端。 (这可以相当于试图/静噪反模式SeanLange提到的。)

对于更复杂的场景,我可以看到TRY ... CATCH的用途。虽然恕我直言,但它无法替代仔细验证输入参数。