繁体   English   中英

如何在内部事务中引发异常而不结束SQL Server存储过程的执行?

[英]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 transactioncatch block内引发exception 在try..catch块之后还有进一步的执行,但是如果执行THROW ,则永远不会执行。 据我了解,至少在SQL Server中,THROW无法区分内部事务和外部事务还是嵌套事务。

在此存储过程中,我有两个表: Tbl1Tbl2 TBL1有一个primary keyTbl1.ID。 TBL2有一个foreign key映射到Tbl1.IDEmpFK。 EmpID具有唯一约束。 不能将重复的记录插入Tbl1中 Tbl1Tbl2都具有ID上的主键,并采用身份增量进行自动插入。 存储的proc具有三个输入参数,其中之一是employeeID

在内部事务中,将一条记录插入Tbl1中 -添加新的员工ID。 如果失败,则认为事务应正常出错,但存储的proc应仍继续运行直到完成。 无论表插入成功还是失败,稍后都会使用EmpID来填充EmpFk

在try..catch块之后,我通过传递给存储proc的employeeID参数执行Tbl1.ID的查找。 然后,我在TBl2中插入一条记录; Tbl1.IDTbl2.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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM