2015-02-10 54 views
1

執行代碼NULL我已經存儲過程與TRY CATCH語句和TRY CATCH內我打電話這是拋出一個錯誤另一個存儲過程。如果錯誤在被調用的存儲過程中,則會引發和捕獲異常,但在ERROR_PROCEDURE()中未顯示,它將設置爲NULL。這似乎是由於動態SQL在被調用的存儲過程中執行所致。ERROR_PROCEDURE()返回由sp_executesql的

ALTER PROC dbo.MyError AS 
BEGIN 
    SET NOCOUNT, XACT_ABORT ON; 
    BEGIN TRY 
     BEGIN TRAN 
      --do stuff here 
      --SQL CODE 
      SELECT 'HELLO' AS hello 

      --then call sproc 
      EXEC dbo.MyInnerError 
     COMMIT TRANSACTION 
    END TRY 
    BEGIN CATCH 
     IF (@@TRANCOUNT > 0) 
      BEGIN 
       ROLLBACK TRANSACTION 
      END 
     SELECT 
      ERROR_NUMBER() AS ErrorNumber, 
      ERROR_SEVERITY() AS ErrorSeverity, 
      ERROR_STATE() AS ErrorState, 
      ERROR_PROCEDURE() AS ErrorProcedure, 
      ERROR_LINE() AS ErrorLine, 
      ERROR_MESSAGE() AS ErrorMessage; 
     END CATCH 
END 
GO 

ALTER PROC dbo.MyInnerError AS 
BEGIN 
    DECLARE @SQl nvarchar(50) = 'SELECT 1/0 as DYNAMIC_FAIL'; 
    EXEC SP_EXECUTESQL @SQl; 
END 

EXEC dbo.MyError 
GO 

我已經嘗試築巢它自己的TRY CATCH的存儲過程,但是這會導致事務回滾的問題。

是ERROR_PROCEDURE NULL,因爲它超出範圍?有沒有辦法設置這個?

這似乎是由於動態SQL在被調用的存儲過程中執行所致。有沒有辦法解決這個問題?

+0

https://sqlserverrider.wordpress.com/2013/09/12/sql-server-get-the-failed-stored-procedure-name-using-error_procedure/ – mohan111 2015-02-10 12:49:38

+0

這就是我比做什麼,但它被設置爲NULL,我正在調用sproc中的sproc。 – davey 2015-02-10 12:53:25

回答

1

編輯基於新的問題和評論

ERROR_PROCEDURE()不會爲通過sp_executesql的執行SQL返回一個程序的名字。從邏輯上講,如果是這樣,它會返回'SP_EXECUTESQL':)。請參閱此連接條目「TRY/CATCH: ERROR_PROCEDURE() does not report name of procedure if error occured in dynamic SQL」,特別是Microsoft在回覆中的這一句話;

由於沒有與特設SQL關聯的名稱,ERROR_PROCEDURE 將來自廣告 特殊SQL的執行層面出現的錯誤返回NULL。


我敲了一個非常快速的測試,它爲我的作品(SQL Server 2012的);

CREATE PROC dbo.MyError AS 
BEGIN 
    SET NOCOUNT, XACT_ABORT ON; 
    BEGIN TRY 
     BEGIN TRAN 
      --do stuff here 
      --SQL CODE 

      --then call sproc 
      EXEC dbo.MyInnerError 
     COMMIT TRANSACTION 
    END TRY 
    BEGIN CATCH 
     IF (@@TRANCOUNT > 0) 
      BEGIN 
       ROLLBACK TRANSACTION 
      END 
     SELECT 
      ERROR_NUMBER() AS ErrorNumber, 
      ERROR_SEVERITY() AS ErrorSeverity, 
      ERROR_STATE() AS ErrorState, 
      ERROR_PROCEDURE() AS ErrorProcedure, 
      ERROR_LINE() AS ErrorLine, 
      ERROR_MESSAGE() AS ErrorMessage; 
     END CATCH 
END 
GO 

CREATE PROC dbo.MyInnerError AS 
BEGIN 
    ;THROW 51000, 'This is my only error.', 1; 
END 
GO 

EXEC dbo.MyError 
GO 

結果是;

ErrorNumber ErrorSeverity ErrorState ErrorProcedure ErrorLine ErrorMessage    
----------- ------------- ----------- ---------------- ----------- ------------------------ 
51000  16   1   MyInnerError  4   This is my only error. 
+0

好的,確實有效!想知道爲什麼我不會調查 – davey 2015-02-10 12:57:37

+0

@davey - 是的,我會將結果添加到我的答案中。 – 2015-02-10 13:00:56

+0

好吧,我的工作不正常,因爲被調用的sproc正在執行動態SQL: – davey 2015-02-10 13:04:33

0

的問題是proc子調用MyInnerError沒有失敗;在MyInnerError內的呼叫失敗,但MyInnerError已完全成功。

MyInnerError正在成功完成,因爲您沒有陷入錯誤並報告失敗,就像您在外部過程中通過TRY/CATCH結構所做的那樣。

這和來自動態SQL的錯誤自然不會設置ERROR_PROCEDURE()

您所有的特效應具有TRY/CATCH結構和CATCH塊應該使用RAISERROR或擲(取決於你使用的是什麼版本的SQL Server),這樣你可以泡錯誤到呼叫範圍。

DECLARE @InNestedTransaction BIT = 0; 

BEGIN TRY 
    IF (@@TRANCOUNT > 0) 
    BEGIN 
    SET @InNestedTransaction = 1; 
    END; 
    ELSE 
    BEGIN 
    BEGIN TRAN; 
    END; 

    ... one or more SQL statements ... 

    COMMIT; 
END TRY 
BEGIN CATCH 
    IF (@InNestedTransaction = 0) 
    BEGIN 
    ROLLBACK; 
    END; 

    IF (ERROR_PROCEDURE() IS NULL) 
    BEGIN 
    DECLARE @ErrMessage NVARCHAR(4000) = ERROR_MESSAGE(), 
      @ErrState TINYINT = ERROR_STATE(), 
      @ErrSeverity TINYINT = ERROR_SEVERITY(); 
    RAISERROR(@ErrMessage, @ErrSeverity, @ErrState); 
    RETURN; 
    END; 

    ;THROW; -- introduced in SQL Server 2012 

    ---- If using SQL Server 2008, replace the above (from "IF" through "THROW") 
    ---- with the following. 
    -- DECLARE @ErrMessage NVARCHAR(4000) = ERROR_MESSAGE(); 
    -- RAISERROR(@ErrMessage, 16, 1); 
    -- RETURN; 
END CATCH; 

IF (ERROR_PROCEDURE() IS NULL)塊被用來捕捉這樣的情況,其中沒有PROC產生誤差。調用;THROW;本身會引起當前錯誤信息,在本例中爲NULLERROR_PROCEDURE()


如果你想這個測試自己,只需要運行下面的SQL它創建3個存儲過程,所有這些都使用上面所示的結構。最內層的程序(ErrorTest1)使用SELECT 1/0;作爲查詢調用sp_executesql。 「除以零」錯誤被TRY/CATCH捕獲。 ERROR_PROCEDURE()函數返回NULL,因爲它是生成錯誤的動態SQL。因此,調用RAISERROR(技術上調用;THROW 50505, @ErrMessage, @ErrState;也可以)以向調用進程指示當前proc生成錯誤。

測試設置:

IF (OBJECT_ID(N'ErrorTest3') IS NOT NULL) 
BEGIN 
    DROP PROCEDURE ErrorTest3; 
END; 
IF (OBJECT_ID(N'ErrorTest2') IS NOT NULL) 
BEGIN 
    DROP PROCEDURE ErrorTest2; 
END; 
IF (OBJECT_ID(N'ErrorTest1') IS NOT NULL) 
BEGIN 
    DROP PROCEDURE ErrorTest1; 
END; 
GO 

CREATE PROCEDURE dbo.ErrorTest1 
AS 
SET NOCOUNT ON; 

DECLARE @InNestedTransaction BIT = 0; 

BEGIN TRY 
    IF (@@TRANCOUNT > 0) 
    BEGIN 
    SET @InNestedTransaction = 1; 
    END; 
    ELSE 
    BEGIN 
    BEGIN TRAN; 
    END; 

    SELECT '1a'; 
    EXEC sp_executesql N'SELECT 1/0 AS [ForceError];'; 
    SELECT '1b'; 

    COMMIT; 

END TRY 
BEGIN CATCH 
    IF (@InNestedTransaction = 0) 
    BEGIN 
    ROLLBACK; 
    END; 

    IF (ERROR_PROCEDURE() IS NULL) 
    BEGIN 
    DECLARE @ErrMessage NVARCHAR(4000) = ERROR_MESSAGE(), 
      @ErrState TINYINT = ERROR_STATE(), 
      @ErrSeverity TINYINT = ERROR_SEVERITY(); 
    RAISERROR(@ErrMessage, @ErrSeverity, @ErrState); 
    RETURN; 
    END; 

    ;THROW; -- introduced in SQL Server 2012 

END CATCH; 
GO 

CREATE PROCEDURE dbo.ErrorTest2 
AS 
SET NOCOUNT ON; 

DECLARE @InNestedTransaction BIT = 0; 

BEGIN TRY 
    IF (@@TRANCOUNT > 0) 
    BEGIN 
    SET @InNestedTransaction = 1; 
    END; 
    ELSE 
    BEGIN 
    BEGIN TRAN; 
    END; 

    SELECT '2a'; 
    EXEC dbo.ErrorTest1; 
    SELECT '2b'; 

    COMMIT; 

END TRY 
BEGIN CATCH 
    IF (@InNestedTransaction = 0) 
    BEGIN 
    ROLLBACK; 
    END; 

    IF (ERROR_PROCEDURE() IS NULL) 
    BEGIN 
    DECLARE @ErrMessage NVARCHAR(4000) = ERROR_MESSAGE(), 
      @ErrState TINYINT = ERROR_STATE(), 
      @ErrSeverity TINYINT = ERROR_SEVERITY(); 
    RAISERROR(@ErrMessage, @ErrSeverity, @ErrState); 
    RETURN; 
    END; 

    ;THROW; -- introduced in SQL Server 2012 

END CATCH; 
GO 

CREATE PROCEDURE dbo.ErrorTest3 
AS 
SET NOCOUNT ON; 

DECLARE @InNestedTransaction BIT = 0; 

BEGIN TRY 
    IF (@@TRANCOUNT > 0) 
    BEGIN 
    SET @InNestedTransaction = 1; 
    END; 
    ELSE 
    BEGIN 
    BEGIN TRAN; 
    END; 

    SELECT '3a'; 
    EXEC dbo.ErrorTest2; 
    SELECT '3b'; 

    COMMIT; 

END TRY 
BEGIN CATCH 
    IF (@InNestedTransaction = 0) 
    BEGIN 
    ROLLBACK; 
    END; 

    SELECT ERROR_PROCEDURE() AS [ErrorProcedure], 
     ERROR_STATE() AS [ErrorState], 
     ERROR_SEVERITY() AS [ErrorSeverity]; 

    IF (ERROR_PROCEDURE() IS NULL) 
    BEGIN 
    DECLARE @ErrMessage NVARCHAR(4000) = ERROR_MESSAGE(), 
      @ErrState TINYINT = ERROR_STATE(), 
      @ErrSeverity TINYINT = ERROR_SEVERITY(); 
    RAISERROR(@ErrMessage, @ErrSeverity, @ErrState); 
    RETURN; 
    END; 

    ;THROW; -- introduced in SQL Server 2012 

END CATCH; 
GO 

運行測試:

EXEC dbo.ErrorTest3; 

返回:

5的結果集:

3a 
2a 
1a 
<empty> 
ErrorProcedure ErrorState ErrorSeverity 
ErrorTest1  1   16 

並在 「信息」 選項卡:

消息50000,級別16,狀態1,過程ErrorTest1,行36
除以零錯誤遇到。

+0

請問一個名爲sproc的try try catch可以識別sproc的名字動態SQL錯誤嗎? – davey 2015-02-12 09:03:39

+0

@davey不僅能夠工作,而且還需要......如果你實現了我在兩個proc中發佈的代碼,你將會看到。 ...另一個答案是不正確的,只能看起來工作,因爲測試是無效的,而不是鏡像你的情況。 – 2015-02-12 14:36:31

+0

@davey我已經更新了我使用的測試代碼。您可以運行它並在更新代碼之前查看它是否有效。 – 2015-02-12 17:53:37