2009-11-04 6 views

Odpowiedz

33

Możesz użyć RAISERROR. Z MSDN documentation na RAISERROR:

BEGIN TRY 
    -- RAISERROR with severity 11-19 will cause execution to 
    -- jump to the CATCH block 
    RAISERROR ('Error raised in TRY block.', -- Message text. 
       16, -- Severity. 
       1 -- State. 
       ); 
END TRY 
BEGIN CATCH 
    DECLARE @ErrorMessage NVARCHAR(4000); 
    DECLARE @ErrorSeverity INT; 
    DECLARE @ErrorState INT; 

    SELECT @ErrorMessage = ERROR_MESSAGE(), 
      @ErrorSeverity = ERROR_SEVERITY(), 
      @ErrorState = ERROR_STATE(); 

    -- Use RAISERROR inside the CATCH block to return 
    -- error information about the original error that 
    -- caused execution to jump to the CATCH block. 
    RAISERROR (@ErrorMessage, -- Message text. 
       @ErrorSeverity, -- Severity. 
       @ErrorState -- State. 
       ); 
END CATCH; 

EDIT:

To naprawdę nie jest to samo jak C# 's throw lub throw ex. Jak zauważa @henrikstaunpoulsen, nie otrzymujesz oryginalnego numeru błędu w nowym błędzie (RAISERROR jest ograniczony, w jakich numerach może go używać). Musiałbyś użyć jakiejś konwencji i przeanalizować informacje (jeśli są dostępne) z wiadomości.

MSDN ma artykuł Using TRY...CATCH in Transact-SQL i użyłem niektóre kodu do stworzenia poniższego testu:

use test; 
GO 

IF OBJECT_ID (N'usp_RethrowError',N'P') IS NOT NULL 
    DROP PROCEDURE usp_RethrowError; 
GO 

CREATE PROCEDURE usp_RethrowError AS 
    IF ERROR_NUMBER() IS NULL 
     RETURN; 

    DECLARE 
     @ErrorMessage NVARCHAR(4000), 
     @ErrorNumber  INT, 
     @ErrorSeverity INT, 
     @ErrorState  INT, 
     @ErrorLine  INT, 
     @ErrorProcedure NVARCHAR(200); 

    SELECT 
     @ErrorNumber = ERROR_NUMBER(), 
     @ErrorSeverity = ERROR_SEVERITY(), 
     @ErrorState = ERROR_STATE(), 
     @ErrorLine = ERROR_LINE(), 
     @ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-'); 

    SELECT @ErrorMessage = 
     N'Error %d, Level %d, State %d, Procedure %s, Line %d, ' + 
      'Message: '+ ERROR_MESSAGE(); 

    RAISERROR 
     (
     @ErrorMessage, 
     @ErrorSeverity, 
     @ErrorState,    
     @ErrorNumber, -- parameter: original error number. 
     @ErrorSeverity, -- parameter: original error severity. 
     @ErrorState,  -- parameter: original error state. 
     @ErrorProcedure, -- parameter: original error procedure name. 
     @ErrorLine  -- parameter: original error line number. 
     ); 
GO 

PRINT 'No Catch' 
DROP TABLE XXXX 

PRINT 'Single Catch' 
BEGIN TRY 
    DROP TABLE XXXX 
END TRY 
BEGIN CATCH 
    EXEC usp_RethrowError; 
END CATCH; 

PRINT 'Double Catch' 
BEGIN TRY 
    BEGIN TRY 
     DROP TABLE XXXX 
    END TRY 
    BEGIN CATCH 
     EXEC usp_RethrowError; 
    END CATCH; 
END TRY 
BEGIN CATCH 
    EXEC usp_RethrowError; 
END CATCH; 

która produkuje następujące dane wyjściowe:

No Catch 
Msg 3701, Level 11, State 5, Line 3 
Cannot drop the table 'XXXX', because it does not exist or you do not have permission. 
Single Catch 
Msg 50000, Level 11, State 5, Procedure usp_RethrowError, Line 25 
Error 3701, Level 11, State 5, Procedure -, Line 7, Message: Cannot drop the table 'XXXX', because it does not exist or you do not have permission. 
Double Catch 
Msg 50000, Level 11, State 5, Procedure usp_RethrowError, Line 25 
Error 50000, Level 11, State 5, Procedure usp_RethrowError, Line 25, Message: Error 3701, Level 11, State 5, Procedure -, Line 16, Message: Cannot drop the table 'XXXX', because it does not exist or you do not have permission. 
+1

Czy nie ma problemu z tym, że liczba błędów jest różni się od pierwotnego wyjątku? –

+0

@henrikstaunpoulsen Tak, masz rację. Nie sądzę, że jest to możliwe w T-SQL lub w jakikolwiek inny sposób. Zaktualizowałem post z dodatkowymi informacjami na ten temat. – ongle

+0

w sql 2012 pojawia się nowe słowo kluczowe THROW – sergiom

0

ja zazwyczaj użyć następujących:

DECLARE @Outcome as bit 
DECLARE @Error as int 

BEGIN TRANSACTION 

-- *** YOUR TSQL TRY CODE HERE **** 


-- Capture the TSQL outcome. 
SET @Error = @@ERROR 

-- Set the Outcome to be returned to the .NET code to successful 
SET @Outcome = 1 


IF @Error <> 0 
    BEGIN 
     -- An Error was generate so we invoke ROLLBACK 
     ROLLBACK 
     -- We set the Outcome to be returned to .Net to unsuccessful 
     SET @Outcome = 0 
    end 

ELSE 
    BEGIN 
     -- The transaction was successful, invoke COMMIT 
     COMMIT 
    END 



-- Returning a boolean value to the .NET code 
Select @Outcome as Outcome 
+0

Jeśli tylko raz sprawdzisz @@ ERROR, czy nie wykryjesz tylko błędu w ostatniej instrukcji twojego kodu SQL? Na przykład. 'WYBIERZ 1/0; WYBIERZ 1; SELECT @@ ERROR' daje błąd równy 0. Użycie @@ BŁĄD wydaje się gorsze niż użycie bloków TRYD. – Jim

2

Oto co Użyłem do przekaż wyjątek po wycofaniu transakcji. Daje to również informację o numerze linii błędu.

BEGIN TRY 
    BEGIN TRANSACTION -- Start the transaction 

    -- Do your work here 

    -- Commit the transaction 
    COMMIT TRANSACTION 

END TRY 

BEGIN CATCH 
    -- There was an error, rollback the transaction 
    IF @@TRANCOUNT > 0 
     ROLLBACK TRANSACTION 

    -- Raise an error with the details of the exception 
    DECLARE @ErrorMessage nvarchar(2048) 
    DECLARE @ErrorProcedure nvarchar(128) 
    DECLARE @ErrorState int 
    DECLARE @ErrorLine int 
    DECLARE @ErrorSeverity int 

    SET @ErrorProcedure = ERROR_PROCEDURE() 
    SET @ErrorLine = ERROR_LINE() 
    SET @ErrorSeverity = ERROR_SEVERITY() 
    SET @ErrorState = ERROR_STATE() 
    SET @ErrorMessage = '' 

    IF @ErrorProcedure IS NOT NULL 
     SET @ErrorMessage = @ErrorMessage + @ErrorProcedure + ' '; 

    IF @ErrorLine IS NOT NULL 
     SET @ErrorMessage = @ErrorMessage + '[Line ' + CAST(@ErrorLine as nvarchar) + '] '; 

    SET @ErrorMessage = @ErrorMessage + ERROR_MESSAGE() 

    RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState) 
END CATCH 
2

w celu uniknięcia powtarzania numerów informacje/błąd/linia postępowania w wielu scenariuszach dotyczących połowów, używam podobną procedurę, z niewielkimi zmianami, co następuje:

IF @Error_Procedure <> OBJECT_NAME(@@PROCID)  
    BEGIN 
     RAISERROR('[Procedure: %s]: Nest Level: %d; Line: %d; Error Number: %d; Message: %s',@Error_Severity,@Error_State,@Error_Procedure, @NestLevel, @Error_Line, @Error_Number, @Error_Message) 
    END 
ELSE 
    BEGIN 
     RAISERROR(@Error_Message,@Error_Severity,@Error_State) 
    END 

Więc jeśli mamy już złapaliśmy i ponownie podnieśliśmy błąd w tym SP, nie dodajemy wielokrotnie dodatkowych informacji, więc w zewnętrznym zakresie widzimy tylko błąd jako pierwotnie ponownie podniesiony.

W przykładach zamieszczonych powyżej, produkcja podwójnego połowu byłaby taka sama jak produkcja pojedynczego połowu. Dołączam także poziom gniazd w komunikacie o błędzie, aby pomóc w debugowaniu.

14

W SQL 2012 dodali nowe słowa kluczowego rzutu, który może być również używany do ponownego rzucać wyjątek

USE tempdb; 
GO 
CREATE TABLE dbo.TestRethrow 
( ID INT PRIMARY KEY 
); 
BEGIN TRY 
    INSERT dbo.TestRethrow(ID) VALUES(1); 
-- Force error 2627, Violation of PRIMARY KEY constraint to be raised. 
    INSERT dbo.TestRethrow(ID) VALUES(1); 
END TRY 
BEGIN CATCH 

    PRINT 'In catch block.'; 
    THROW; 
END CATCH; 

http://msdn.microsoft.com/en-us/library/ee677615.aspx

Powiązane problemy