簡體   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