【问题标题】:SQL Server error handling: exceptions and the database-client contractSQL Server 错误处理:异常和数据库客户端契约
【发布时间】:2010-10-20 04:48:08
【问题描述】:

我们是一个由 SQL Server 数据库开发人员组成的团队。我们的客户混合了 C#/ASP.NET、C# 和 Java Web 服务、Java/Unix 服务和一些 Excel。

我们的客户端开发人员只使用我们提供的存储过程,并且我们希望(当然,在合理的情况下)他们将它们视为 Web 服务方法。

我们的一些客户端开发人员不喜欢 SQL 异常。他们用自己的语言理解它们,但他们不明白 SQL 在我们沟通问题的方式上是有限的。

我不仅仅指 SQL 错误,例如尝试将“bob”插入 int 列。

我也指例外情况,例如告诉他们参考值错误,或者数据已经更改,或者他们不能这样做,因为他的聚合不为零。

他们实际上并没有任何具体的替代方案:他们提到我们应该输出参数,但我们假设异常意味着“处理停止/回滚。

这里的人们如何处理数据库-客户端合同?无论是一般情况下,还是在数据库和客户端代码猴子之间存在分离的情况下。

编辑:

  • 我们只使用 SQL Server 2005 TRY/CATCH
  • 我们已经将回滚后的所有错误记录到异常表中
  • 我们担心我们的一些客户不会检查输出参数并假设一切正常。我们需要标记错误以供支持查看。
  • 一切都是异常...客户端需要进行一些消息解析以区分信息与错误。为了将我们的异常与数据库引擎和调用错误分开,它们应该使用错误号(我们当然都是 50,000)

【问题讨论】:

  • “我们担心我们的一些客户不会检查输出参数并假设一切正常。我们需要标记错误以供支持查看。”除了发送电子邮件之外,我想不出在 T-SQL 中执行此操作的方法!
  • 客户端例程为任何异常(我们或他们)发送电子邮件...我们的公司构建不允许以任何方式来自 SQL Server 的电子邮件...

标签: sql-server-2005 tsql sql-server-2008 exception stored-procedures


【解决方案1】:

我是一个经常处理数据库的客户端代码猴子。这是我的处理方式。

SQL 中发生的异常(引发错误)会传播回调用者。这将包括 ref 约束、唯一索引违规、更严重的问题等。基本上任何不会使数据操作正常发生的事情都应该传播回来。

调用者 C# 应该有这个:

catch (SQLException sqlEx)

然后根据需要处理异常。他们应该有一个特定的 SQLException 处理程序。这很重要。

我通常远离输出参数,因为我认为这些参数与正在传输的数据有关,而不是任何错误消息,另外我可以检查 SQL Server 错误代码的异常,因此我们需要的所有数据都应该在其中例外。

此外,在 SQL Server 的某些情况下,我们有可能引发“业务类型异常”的存储过程。在这些情况下,我们添加一个自定义错误编号(50000 以上)并在需要时在存储过程中引发该错误。一般来说,我们尽量将这些保持在最低限度,因为它增加了复杂性,但在某些情况下,我们发现它们是必要的。

现在,由于客户端正在捕获 SQLException,因此他们可以查看 SQL Server 在异常中返回的错误代码,然后在捕获到异常并且错误编号为某个值时采取任何特殊操作(如果需要) .如果自定义错误 (>50000) 需要,这允许基于错误代码的二级错误处理。

这还允许 DBA 引发自定义错误,并让客户端代码以一致的方式处理它们。然后,DBA 必须告诉客户端代码猴子自定义错误是什么,以便他们为它们做好准备。

我通常不将返回码用于错误处理目的,虽然我可以看到它们是如何使用的,但这意味着代码猴子层中有更多的逻辑来查看和处理返回码。如果它们是一个问题,我想要一个例外,因为这样我就可以始终如一地处理它们。如果我还必须查看返回码,那么现在有多种错误处理途径。

【讨论】:

  • 和我们进化的一样。谢谢你。我们还开始使用 RAISERROR 中的 State 来粗略地模拟我们新的 REST 内容的 http 错误代码,例如 SQL 中的 100 映射到 409 以表示唯一失败。
【解决方案2】:

我通常使用输出参数 - 并定义可能的值。

0 = 成功
正整数(假设在插入时)= 新行 ID (@@identity)

负整数 = 已知可能的错误情况
-1 = 缺少 @LastName(或零长度)
-2 = 缺少@FirstName(或零长度)
...等等...

定义这变得有点乏味 - 但它可以扩展 - 它是一种将有意义的结果返回给客户的方法,并且数据访问层可以将失败的确切原因传递回业务对象层(我在业务对象层中使用枚举来处理不同的状态条件)。

如果需要,数据访问层也可以抛出异常 - 但我认为从性能角度来看,如果您可以检查枚举或整数值,最好不要抛出错误。

还有其他技术 - 这只是我过去使用过的一种,取得了一定的成功。

【讨论】:

  • 谢谢。我们考虑了这一点,但我们可能还需要抛出文本和值,例如聚合中的最新值。
【解决方案3】:

这是我的建议:

一切正常时返回 0
当发生逻辑错误、丢失或无效数据时返回负 x
当发生致命错误、插入失败等时返回正 x。

将输出参数 ErrorMsg 和 ErrorLog 添加到存储过程中。 ErrorMessage 将包含人类可读的消息,ErrorLog 将包含调试信息。
如果返回 0,这些将为 NULL
您可以使用 ErrorLog 记录任何问题,确保如果将它们插入到表中,则在回滚后执行。

使用 TRY - Catch 捕获问题,构建您自己的消息,并使用上述约定返回信息。

【讨论】:

  • 我们总是使用 TRY/CATCH 并记录异常(回滚后)
【解决方案4】:

在我目前的工作中,我们为异常条件保留例外。对于诸如错误参数之类的事情,我们有标准的返回码——例如,无效的参数值总是返回 98。 (为什么是 98?那已经被历史遗忘了……)

这不一定是理想的 b/c 客户端和 SP 都必须了解特定返回代码的含义;这是一个糟糕的关注点分离。

在特殊情况下,我们使用 OUT 参数返回消息,而我们的批处理服务使用标准化的 #scratch 表设置。

在之前的雇主中,我们出于同样的目的使用了自定义 SQL 错误。所以,使用有意义的东西。就个人而言,我更喜欢单一机制,但这可能取决于您的环境。

也许你们都应该聚在一起为客户端代码开发标准化的方法来处理它。 Excel 中使用的 VBA 具有 C# 和 Java 代码所没有的明显限制,并且尽管 Java 和 C# 彼此相似,但它们并不相同。 如果您要坚持返回异常,那么如果您可以同意 a) 指示“错误”异常的已知消息 # 范围和 b) 标准化消息布局,以便可以进行标准化解析,开发人员将完成 90%。

【讨论】:

    猜你喜欢
    • 2018-02-10
    • 1970-01-01
    • 2011-02-19
    • 2019-06-09
    • 2015-08-18
    • 1970-01-01
    • 2016-04-11
    • 1970-01-01
    • 2015-01-14
    相关资源
    最近更新 更多