【问题标题】:Should I put error handling in T-SQL?我应该在 T-SQL 中进行错误处理吗?
【发布时间】:2011-12-01 01:33:45
【问题描述】:

作为开发人员,错误处理和 try/catch 是我编写的代码中非常重要的部分。

但是,在 SQL Server 存储过程中,在 SP 中编写错误处理是最佳实践吗?如果没有(这似乎是常见情况),异常是否会传播到 .NET 代码?我问这个问题是因为我认为 T-SQL 的行为类似于 C# 错误处理。

在 T-SQL 中编写错误处理的最佳方法是什么?

【问题讨论】:

  • 只有在合适的情况下你才“应该”。
  • 请阅读这篇由 SQL Server MVP Erland Sommarskog 撰写的文章:Error Handling in SQL Server (2000 & 2005)
  • @p.campbell 我同意 - 也许对适当(而不是)案例场景的一个很好的解释会有所帮助。

标签: c# .net sql-server tsql


【解决方案1】:

是的,在您的 TSQL 代码中使用 TRY...CATCH。在这种情况下,异常不会传播到客户端。

每当您使用RAISEERROR 或出现未处理的 SQL 异常时,都会发生传播。

BEGIN TRY
     SELECT Foo FROM MyLinkedServer.dbo.SomeTable; --what if the remote server is down?

     DECLARE @Baz uniqueidentifier  = 15/2; --oops        
END TRY
BEGIN CATCH
    SELECT @Baz = NEWID();
    ROLLBACK TRAN; 
    --whatever business logic you have for handling your exception.
END CATCH

【讨论】:

  • 这样做有什么好处?除非您实际上是在 处理 错误(即它不是 SQL 中的错误),否则让(未处理的)错误使调用者爆炸似乎更好。
  • @Kirk 我愚蠢的演示代码没有多大意义。也许您想回滚事务。也许您想发送一些 SQLMail。也许您想在另一个链接服务器中查找一些数据。也许您想将其记录在审核表中。
  • 感谢您的回复,很公平。 (虽然说真的,我同意 Dylan 的观点,即业务逻辑不属于这里)
  • 业务逻辑可能有也可能没有,但处理实际的 SQL Server 错误(即使是数据类型不匹配、数学错误等)肯定属于那里。通常我使用TRY...CATCH 块来当数据库端需要发生其他事情(例如日志记录、回滚等)时,然后说“嘿,这里发生了一些事情!”。我们捕获错误,然后将错误报告给调用者让他们随心所欲地处理它。
  • @Wil,IMO,您提到的所有内容都最好在调用站点而不是 SQL 中实现。我宁愿登录呼叫站点。我宁愿在调用点回滚。无论如何,错误都会返回给调用者,为什么要在传输过程中修改它?
【解决方案2】:

我喜欢在我的 SQL 和代码库中保留尽可能多的逻辑。涉及“如果发生此异常,我想采取此操作”的逻辑,听起来像是我希望在我的代码中包含的逻辑。

所以我通常会让异常传播到代码并在那里处理它。

顺便说一句,根据我的经验,人们使用 try/catch 的次数太多了。只有在出现错误的情况下您确实想要做不同的事情时才尝试/捕获。如果您只想应用通用错误记录类型代码,让它一直传播到顶部并在一个地方处理它。更糟糕的是,当人们在他们的代码中乱扔垃圾时,会无缘无故地吞下异常。

【讨论】:

    【解决方案3】:

    我不知道有非黑即白的选择。总会有取舍。

    除非要采取明确的恢复措施,否则我看不出重点。

    如果数据库由多个客户端共享,也许这是有道理的。否则将强制每个客户端实现错误处理逻辑。在这种情况下,问题是应该将什么发回给客户?当然,他们应该知道发生了什么以及为什么。

    答案取决于您是 DBA 还是中间层开发人员。每个人都可能争辩说最好把责任留给他们。

    【讨论】:

      【解决方案4】:

      我的偏好是让数据库抵御“坏”数据。 (曾经有人建议我应该针对 10% 的错误数据进行设计。控制工程师设计了一台自动化机器。哦。)因此,我尝试使用索引、外键、约束和触发器来捕捉我能捕捉到的东西。所有访问都是通过存储过程,给予或接受播放 SSMS 的向导。

      在存储过程中,我倾向于不太担心语句失败,而是担心正在发生的事情是有意义的。输入验证很常见,通常是这样的:

      -- Validate the input.
      if @Serial is NULL
          begin
          rollback transaction
          RaIsError( '@Serial   cannot be   NULL .', 13, 0 )
          return
          end
      set @TelephoneNumber = LTrim( RTrim( @TelephoneNumber ) )
      if @TelephoneNumber is NULL or @TelephoneNumber = ''
          begin
          RaIsError( '@TelephoneNumber   must be supplied.', 13, 0 )
          return
          end
      

      (顺便说一句:当使用 Sun God boo boo,RaIsError 时,严重性需要 >=11 才能获得向 .NET 抛出的 SQL 异常。也许我们都可以加入并为 Microsoft 买一个元音。)

      同样,我会根据需要检查中间结果。例如,如果我要查找现存的独特的东西,但发现行数不是 1,那么我就会出错。

      【讨论】:

        【解决方案5】:

        视情况而定。

        通常情况下,如果它是一个非常包容的存储过程,可以处理几乎所有的逻辑分支工作,那么我肯定会捕获错误并在该存储过程中相应地处理它们。

        但有时,如果它只是一个快速调用或查询,我会让错误出现在应用程序中,以便“轻松”读取错误并在应用程序代码中进行处理。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-07-07
          • 1970-01-01
          • 1970-01-01
          • 2019-10-13
          • 2016-09-05
          • 2014-02-05
          • 2010-11-24
          • 1970-01-01
          相关资源
          最近更新 更多