【发布时间】:2019-07-06 13:19:33
【问题描述】:
我的目标是向调用者抛出异常,但继续执行SQL Server 存储过程。所以,本质上,我想要完成的是try..catch..finally block,尽管据我所知,SQL Server 没有 try..catch..finally 块的概念。
我有一个示例stored procedure 来说明。这只是我想出的一个例子,所以请不要过多关注表模式。希望您了解我在这里尝试执行的操作的要点。无论如何,存储过程包含一个explicit transaction,它在catch block 中抛出一个exception。在 try..catch 块之后还有进一步的执行,但如果 THROW 被执行,它永远不会被执行。据我了解,至少在 SQL Server 中,THROW 无法区分内部事务和外部事务或嵌套事务。
在这个存储过程中,我有两个表:Tbl1 和 Tbl2。 Tbl1 在 Tbl1.ID 上有一个primary key。 Tbl2 在 EmpFK 上有一个 foreign key,它映射到 Tbl1.ID。 EmpID 有一个唯一的约束。 Tbl1 中不能插入重复记录。 Tbl1 和 Tbl2 在 ID 上都有主键,并使用身份增量进行自动插入。存储过程有三个输入参数,其中一个是employeeID。
在内部事务中,Tbl1 中插入了一条记录——添加了一个新的员工 ID。如果它失败了,这个想法是事务应该优雅地出错,但存储的过程应该仍然继续运行直到完成。无论表格插入成功与否,后面都会使用EmpID来填写EmpFk。
在 try..catch 块之后,我通过传递到存储过程的 employeeID 参数执行 Tbl1.ID 查找。然后,我在 TBl2 中插入一条记录; Tbl1.ID 是 Tbl2.EmpFK 的值。
(您可能会问“为什么要使用这样的模式?为什么不将这么小的数据集合并到一个表中?”同样,这只是一个示例。它不一定是员工。你可以选择任何东西. 它只是一个小部件。想象一下 Tbl1 可能包含一个非常非常大的数据集。确定的是有两个表具有主键/外键关系。)
这是示例数据集:
Tbl1
ID EmpID
1 AAA123
2 AAB123
3 AAC123
Tbl2
ID Role Location EmpFK
1 Junior NW 1
2 Senior NW 2
3 Manager NE 2
4 Sr Manager SE 3
5 Director SW 3
这是示例存储过程:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[usp_TestProc]
@employeeID VARCHAR(10)
,@role VARCHAR(50)
,@location VARCHAR(50)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @employeeFK INT;
BEGIN TRY
BEGIN TRANSACTION MYTRAN;
INSERT [Tbl1] (
[EmpID]
)
VALUES (
@employeeID
);
COMMIT TRANSACTION MYTRAN;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
BEGIN
ROLLBACK TRANSACTION MYTRAN;
END;
THROW; -- Raises exception, exiting stored procedure
END CATCH;
SELECT
@employeeFK = [ID]
FROM
[Tbl1]
WHERE
[EmpID] = @employeeID;
INSERT [Tbl2] (
[Role]
,[Location]
,[EmpFK]
)
VALUES (
@role
,@location
,@employeeFK
);
END;
所以,我仍然想将错误返回给调用者,即记录错误,但我不希望它停止冷执行存储过程。它应该继续非常类似于 try..catch..finally 块。这可以通过THROW 完成吗?还是我必须使用其他方法?
也许我弄错了,但THROW 不是RAISERROR 的升级版,而且我们应该使用前者来处理异常?
我过去曾在这些情况下使用过RAISERROR,它非常适合我。但是THROW 是一个更简单、更优雅的解决方案,imo,并且可能是更好的实践。我不太确定。
提前感谢您的帮助。
【问题讨论】:
-
RAISERROR没有被弃用,不,因此您可以使用它来实现此行为。 (DB<>Fiddle 实际上无法在此处复制行为,但是,如果您在自己的实例上使用此 SQL,您将看到返回 2 个结果集,一个没有行,一个有) -
这只是在尖叫成为xy problem。
-
@Larnu。谢谢你。在我的 SQL Server 实例上,抛出异常并且第二次插入从未完成。据我了解,SQL Server 不会在事务之间解析抛出的异常。因此,
THROW退出存储过程。如果不是这样,我将回到绘图板,看看我是否可以在我的实例中复制 DB Fiddle。再次感谢您的帮助。 -
@SeanLange 我不太清楚你为什么这么说。这不是面试问题,但我经常看到这种时尚的面试问题。如果我在面试中被问到这个确切的问题,我会尽我所知回答这个问题。我无法回答这是一个 XY 问题。我必须解决它。如果问题被定义为如何在不停止执行的情况下抛出,那就是手头的任务,那么它就不是 XY 问题。
-
您认为您可以向调用者抛出异常并继续执行吗?
try/catch/finally通常不是这样定义的。
标签: sql-server exception stored-procedures transactions throw