T-SQL 抛出异常并返回(不使用 TRY)

Posted

技术标签:

【中文标题】T-SQL 抛出异常并返回(不使用 TRY)【英文标题】:T-SQL Throw Exception with a Return (not using TRY) 【发布时间】:2018-08-21 21:29:45 【问题描述】:

我使用的是 SQL Server 2014。

编辑:我想我应该更清楚。我确实明白,通过将所有内容放在 TRY/CATCH 中,它将执行我希望它执行的方式。对于这个问题,我主要只是询问第一个示例是否是不好的做法,以及是否有办法在不使用 RETURN 或 TRY/CATCH 块的情况下引发导致终止的错误。在查看当前答案和链接文档之后,我认为答案是......是的,这是不好的做法,不,您不能仅使用 THROW 函数以失败而终止。


我已经编写了一个过程,我希望在其余代码运行之前进行一些预检查。例如,我有一个为特定客户运行的服务,一个表控制它应该为哪些客户运行,另一个表控制是否启用该服务本身。

这是我刚刚汇总的一个示例。它检查以确保服务已启用,并且还确保为该特定客户启用服务。如果不是,则 proc 退出并出现错误。并且还希望将失败返回给可能运行此过程的任何作业。

我在 THROW 或 TRY/CATCH 方面没有太多经验,但我只是想确保我不会在这里遗漏一些东西。我发现 THROW 不会终止 proc,除非它位于 TRY 块中。因此,由于我在 TRY 之外使用 THROW,我是否通过在 THROW 之后使用 RETURN 来正确使用它?

DECLARE @CustomerID INT = 10000 --Proc Parameter

DECLARE @ServiceID INT = 1

IF NOT EXISTS (SELECT * FROM dbo.Service WHERE ServiceID = @ServiceID AND [Enabled] = 1)
BEGIN
    ;THROW 51000, 'Service is not enabled', 1;
    RETURN;
END
IF NOT EXISTS (SELECT * FROM dbo.CustomerService WHERE CustomerID = @CustomerID AND ServiceID = @ServiceID AND [Enabled] = 1)
BEGIN
    ;THROW 51000, 'Customer does not have service enabled', 1;
    RETURN;
END

--Do stuff

旁注:我真的只是在问我是否以这种方式错误地使用了 THROW,或者是否有一种使用 THROW 的方法会导致失败/返回。

编辑/更新: 我想我可以这样写,但不确定这是否是一种奇怪的 SQL 编写方式。

DECLARE @CustomerID INT = 10000 --Proc Parameter

DECLARE @ServiceID INT = 1

BEGIN TRY
    IF NOT EXISTS (SELECT * FROM dbo.Service WHERE ServiceID = @ServiceID AND [Enabled] = 1)
        THROW 51000, 'Service is not enabled', 1;

    IF NOT EXISTS (SELECT * FROM dbo.CustomerService WHERE CustomerID = @CustomerID AND ServiceID = @ServiceID AND [Enabled] = 1)
        THROW 51000, 'Customer does not have service enabled', 1;
END TRY
BEGIN CATCH
    THROW;
END CATCH

--Do Stuff

【问题讨论】:

【参考方案1】:

我建议在异常处理和实现的上下文中专门使用 throw。还有其他更好的方法来执行预检查,您可以使用输出参数或日志来返回和/或记录某些检查。 check this link

这是来自 msdn,看来 throw 将终止对您的查询的进一步处理:

“引发异常并将执行转移到 SQL Server 2017 中 TRY…CATCH 构造的 CATCH 块。”

如果 TRY…CATCH 构造不可用,则语句批处理将终止。设置引发异常的行号和过程。严重性设置为 16。”

【讨论】:

【参考方案2】:

由于返回命令

从查询或过程中无条件退出。 RETURN 是立即且完整的,可以在任何时候用于退出过程、批处理或语句块。 RETURN 后面的语句不会被执行。 SQL Docs

您可以使用此方法而不是 TRY...CATCH 来停止存储过程。使用 TRY...CATCH 如下所示:

BEGIN TRY
DECLARE @CustomerID INT = 10000 --Proc Parameter

DECLARE @ServiceID INT = 1

IF NOT EXISTS (SELECT * FROM dbo.Service WHERE ServiceID = @ServiceID AND [Enabled] = 1)
    THROW 51000, 'Service is not enabled', 1;
IF NOT EXISTS (SELECT * FROM dbo.CustomerService WHERE CustomerID = @CustomerID AND ServiceID = @ServiceID AND [Enabled] = 1)
    THROW 51000, 'Customer does not have service enabled', 1;
--Do stuff
END TRY
BEGIN CATCH
    THROW
END CATCH

自从

如果指定的 THROW 语句不带参数,则它必须出现在 CATCH 块内。这会导致引发捕获的异常。 SQL Docs

【讨论】:

【参考方案3】:

THROW 语句会终止整个批处理(除非使用外部异常处理程序处理),因此在THROW 之后不需要RETURN

DECLARE @CustomerID INT = 10000; --Proc Parameter

DECLARE @ServiceID INT = 1;

BEGIN TRY

    IF NOT EXISTS (SELECT * FROM dbo.Service WHERE ServiceID = @ServiceID AND [Enabled] = 1)
    BEGIN
        THROW 51000, 'Service is not enabled', 1;
    END;
    IF NOT EXISTS (SELECT * FROM dbo.CustomerService WHERE CustomerID = @CustomerID AND ServiceID = @ServiceID AND [Enabled] = 1)
    BEGIN
        THROW 51000, 'Customer does not have service enabled', 1;
    END;
    --do stuff
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0 ROLLBACK; --this is needed when the TRY block includes a BEGIN TRAN and COMMIT
    THROW;
END CATCH;

【讨论】:

【参考方案4】:

你可以在没有 try catch 和 RETURN 的情况下使用 throw

DECLARE @CustomerID INT = 10000 --Proc Parameter

DECLARE @ServiceID INT = 1

IF NOT EXISTS (SELECT * FROM dbo.Service WHERE ServiceID = @ServiceID AND [Enabled] = 1)
    THROW 51000, 'Service is not enabled', 1;

IF NOT EXISTS (SELECT * FROM dbo.CustomerService WHERE CustomerID = @CustomerID AND ServiceID = @ServiceID AND [Enabled] = 1)
    THROW 51000, 'Customer does not have service enabled', 1;

--do stuff

【讨论】:

以上是关于T-SQL 抛出异常并返回(不使用 TRY)的主要内容,如果未能解决你的问题,请参考以下文章

T-SQL编程中的异常处理-异常捕获(try catch)与抛出异常(throw)

T-SQL 抛出异常

解析Java-throw抛出异常详细过程

try-catch和throw,throws的区别

在锁 c#2 中抛出异常

当try块中有多个异常发生时,如何处理?