[英]How to THROW exception in inner transaction without ending SQL Server stored procedure execution?
我的目标是将异常返回给调用者,但继续执行SQL Server
存储过程。 因此,从本质上讲,据我所知,即使SQL Server没有try..catch..finally块的概念,我想要完成的也是try..catch..finally block
。
我有一个示例stored procedure
来说明。 这只是我想到的一个例子,所以请不要过多地关注表模式。 希望您了解我正在尝试执行的要点。 无论如何,存储的proc包含一个explicit transaction
,该explicit transaction
在catch block
内引发exception
。 在try..catch块之后还有进一步的执行,但是如果执行THROW
,则永远不会执行。 据我了解,至少在SQL Server中,THROW无法区分内部事务和外部事务还是嵌套事务。
在此存储过程中,我有两个表: Tbl1和Tbl2 。 TBL1有一个primary key
上Tbl1.ID。 TBL2有一个foreign key
映射到Tbl1.ID上EmpFK。 EmpID具有唯一约束。 不能将重复的记录插入Tbl1中 。 Tbl1和Tbl2都具有ID上的主键,并采用身份增量进行自动插入。 存储的proc具有三个输入参数,其中之一是employeeID 。
在内部事务中,将一条记录插入Tbl1中 -添加新的员工ID。 如果失败,则认为事务应正常出错,但存储的proc应仍继续运行直到完成。 无论表插入成功还是失败,稍后都会使用EmpID来填充EmpFk 。
在try..catch块之后,我通过传递给存储proc的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,并且可能是以后的更好实践。 我不太确定
提前谢谢你的帮助。
刻板的是有两个具有主键/外键关系的表。
在内部事务中使用THROW并不是实现您想要的方式。 从您的代码判断,您想要插入一个新员工,除非该员工已经存在,然后,无论该员工是否已经存在,您都希望在该子表的第二次插入中使用该员工的PK / id。
一种方法是拆分逻辑。 这是我的意思的伪代码:
IF NOT EXISTS(Select employee with @employeeId)
INSERT the new employee
SELECT @employeeFK like you are doing.
INSERT into Table2 like you are doing.
如果在传递已经存在的@employeeId时仍然需要引发错误,则可以在IF后面放置一个ELSE,并填充一个字符串变量,如果在proc末尾填充了该变量,则抛出/引发错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.