什么是使存储的proc足够健壮以使其可以很好地扩展并包含错误处理的好方法?
此外,在存储的proc中处理多个错误情况并拥有智能反馈系统的最佳方法是什么,该系统将有意义的错误信息返回给调用的应用程序?
什么是使存储的proc足够健壮以使其可以很好地扩展并包含错误处理的好方法?
此外,在存储的proc中处理多个错误情况并拥有智能反馈系统的最佳方法是什么,该系统将有意义的错误信息返回给调用的应用程序?
Answers:
亚历克斯·库兹涅佐夫(Alex Kuznetsov)在其《防御性数据库编程》(第8章)中有很长的一章,内容涉及T-SQL TRY ... CATCH,T-SQL事务和SET XACT_ABORT设置,以及使用客户端错误处理。这将帮助您确定哪种选择最适合您需要完成的工作。
它可用于免费在这个网站。我绝不隶属于该公司,但我确实拥有该书的印刷版。
亚历克斯很好地解释了这个问题上的许多小细节。
根据Nick的要求...(但并非所有内容都在本章中)
在扩展方面,您必须坦率地说哪些活动需要在数据库代码中,哪些活动应在应用程序中。是否曾经注意到快速执行的代码趋向于回到针对每种方法进行单个关注的设计?
最简单的通信方式是自定义错误代码(> 50,000)。它也非常快。这确实意味着您必须使数据库代码和应用程序代码保持同步。使用自定义错误代码,您还可以在错误消息字符串中返回有用的信息。因为您严格地针对这种情况使用了错误代码,所以您可以在应用程序代码中为错误的数据格式量身定制解析器。
另外,哪些错误条件需要数据库中的重试逻辑?如果您想在X秒钟后重试,那么最好在应用程序代码中进行处理,以免事务阻塞太多。如果您只是立即重新提交DML操作,则在SP中重复执行它可能会更有效率。但是请记住,您可能必须复制代码或添加一层SP才能重试。
确实,这是目前SQL Server中TRY ... CATCH逻辑的最大难题。可以做到,但是有点麻烦。在SQL Server 2012中寻找一些改进,尤其是重新抛出系统异常(保留原始错误号)。另外,还有FORMATMESSAGE,它在构造错误消息时增加了一些灵活性,尤其是用于记录目的。
这是我们的模板(错误记录已删除)
笔记:
...因此创建的TXN不会超出您的需要
然而,
CREATE PROCEDURE [Name]
AS
SET XACT_ABORT, NOCOUNT ON
DECLARE @starttrancount int
BEGIN TRY
SELECT @starttrancount = @@TRANCOUNT
IF @starttrancount = 0
BEGIN TRANSACTION
[...Perform work, call nested procedures...]
IF @starttrancount = 0
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0 AND @starttrancount = 0
ROLLBACK TRANSACTION
RAISERROR [rethrow caught error using @ErrorNumber, @ErrorMessage, etc]
END CATCH
GO
我使用“尝试/捕获”,但我还会收集尽可能多的信息,并在回滚后将其写入错误日志。在此示例中,“ LogEvent”是一个存储过程,它写入EventLog表,其中包含发生的事件的详细信息。GetErrorInfo()是一个函数调用,返回确切的错误消息。
当发生错误时,将收集信息,该过程将跳至错误处理部分并发出回滚。该信息将被写入日志,然后该过程退出。
考虑到所涉及的额外过程/函数调用,这似乎有点过头了。但是,在尝试调试问题时,此方法非常有用。
exec LogEvent @ Process,@ Database,“尝试插入等等等等” 开始尝试 插入MyTable 选择值 从MyOtherTable 选择@rowcount = @@ ROWCOUNT 结束尝试 -错误处理 开始比赛 选择@error = ERROR_NUMBER(), @rowcount = -1, @TableAction ='插入', @TableName = @数据库+'.MyTable', @AdditionalInfo ='(尝试插入等等)'+ dbo.GetErrorInfo() 转到TableAccessError 结束观看 。 。 。 。 TableAccessError: 如果(@@ TRANCOUNT> 0)回滚 选择@output = upper(@TableAction)+ '错误-'+时发生错误 案例(@TableAction) 当“更新”然后“更新” 当“删除”然后“删除” 否则@TableAction +'ing' 结束+ '记录'+ 案例(@TableAction) 当“选择”然后“从” 当'更新'然后'在' 当“插入”然后“插入”时 其他“来自” 结束+ ''+ @TableName +'表。 选择@output = @output +'@@ ERROR:'+ convert(varchar(8),@ error) 选择@output = @output +'@@ ROWCOUNT:'+ convert(varchar(8),@ rowcount) 选择@output = @output + isull(@AdditionalInfo,'') exec LogEvent @ Process,@ Database,@ Output RAISERROR(@ output,16,1)与日志 选择@ReturnCode = -1 转到THE_EXIT