我应该在T-SQL中加入错误处理吗?

3
作为一名开发人员,错误处理和try/catch是我编写代码的非常重要的部分。
然而,在SQL Server存储过程中,最好的做法是在SP内部编写错误处理吗?如果没有(这似乎是普遍情况),异常是否会传播到.NET代码?我提出这个问题是因为我认为T-SQL的行为类似于C#错误处理。
那么,在T-SQL中编写错误处理的最佳方法是什么?

1
只有在适当的情况下才应该这样做。 - p.campbell
请阅读Erland Sommarskog,SQL Server MVP撰写的文章:SQL Server(2000&2005)中的错误处理 - Bogdan Sahlean
@p.campbell 我同意 - 也许提供一个合适(或不合适)案例情形的好解释会有所帮助。 - Falcon
5个回答

5

是的,在你的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 Woll
@Kirk,我的愚蠢演示代码没有太多意义。也许你想回滚一个事务。也许你想发送一些SQL邮件。也许你想在另一个链接服务器中查找一些数据。也许你想将其记录在审计表中。 - p.campbell
谢谢回复,说得好。尽管我真的同意Dylan的观点,即业务逻辑不应该放在这里。 - Kirk Woll
1
业务逻辑可能需要,也可能不需要,但处理实际的SQL Server错误(即使是像数据类型不匹配、数学错误等)肯定属于其中。通常我使用TRY...CATCH块来处理数据库端需要发生其他事情的情况,比如日志记录、回滚等,然后说“嘿,这里发生了什么!”我们捕获错误,然后将错误报告给调用者,并让他们以任何方式处理它。 - Wil
@Wil,我个人认为你提到的所有内容最好都在调用点实现,而不是在SQL中。我宁愿在调用点记录日志。我宁愿在调用点回滚。错误将无论如何返回给调用者,为什么要在传输过程中修改它呢? - Kirk Woll

2
我喜欢尽可能将逻辑放在代码库中,而不是SQL中。涉及“如果发生此异常,我想要执行此操作”的逻辑听起来更适合放在我的代码中。
因此,我通常会让异常传播到代码中并在那里处理它。
顺便说一句,根据我的经验,人们过度使用try/catch。只有在出现错误时确实想要进行不同的操作时才使用try/catch。如果您只想应用通用错误记录类型代码,请让其一直传播到顶部并在那里处理它。更糟糕的是,当人们为没有充分理由的情况下使用try/catch来吞噬异常时,这会使他们的代码变得混乱。

1

我的偏好是让数据库自己防御“坏”数据。(曾经有人建议我设计时要考虑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

(顺便说一句:当使用太阳神boo boo,RaIsError时,严重程度需要>=11才能抛出SQL异常到.NET。也许我们可以集资给微软买个元音字母。)

同样地,我会根据需要检查中间结果。例如,如果我应该查找某个现有且唯一的东西,但发现行数不是一个,则我会退出。


1

我不知道是否有黑白之分。总是需要权衡利弊。

除非有明确的恢复操作需要执行,否则我看不出有什么意义。

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

答案将取决于您是DBA还是中间层开发人员。每个人都可能会认为责任最好留给他们。


0

这要看情况。

通常,如果它是一个非常全面的存储过程,几乎处理所有逻辑工作,那么我一定会在该存储过程中捕获错误并相应地处理。

但有时,如果只是一个快速调用或查询,我会让错误显示在应用程序中,以便在应用程序代码中更容易阅读错误并进行处理。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接