2009-11-04 2 views
26

Le titre est vraiment la question pour celui-ci: Y at-il un équivalent en T-SQL à C# "jeter"? Rejeter des exceptions?Existe-t-il un équivalent en T-SQL au "throw" de C#? Rejeter des exceptions?

En C# on peut le faire:

try 
{ 
    DoSomethingThatMightThrowAnException(); 
} 
catch (Exception ex) 
{ 
    // Do something with the exception 
    throw; // Re-throw it as-is. 
} 

Y at-il quelque chose dans BEGIN CATCH T-SQL fonctionnalité qui fait la même chose?

Répondre

33

Vous pouvez utiliser RAISERROR. De l'MSDN documentation sur 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:

Ce n'est pas vraiment la même chose que C# 's throw ou throw ex. Comme @henrikstaunpoulsen souligne que vous n'obtenez pas le numéro d'erreur d'origine dans la nouvelle erreur (RAISERROR est limité dans quels numéros il peut utiliser). Vous devriez utiliser une sorte de convention et analyser l'information (si disponible) hors du message.

MSDN a un article Using TRY...CATCH in Transact-SQL et j'ai utilisé une partie du code pour créer le test ci-dessous:

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; 

qui produit la sortie suivante:

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

est-il pas un problème avec cela, en ce que le numéro d'erreur est différent de l'exception originale? –

+0

@henrikstaunpoulsen Oui, vous avez raison. Je ne pense pas que ce soit possible dans T-SQL ou d'une autre manière. J'ai mis à jour le poste avec plus d'informations sur ce point. – ongle

+0

en SQL 2012 il y a le nouveau mot-clé THROW – sergiom

0

J'utilise généralement les éléments suivants:

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

Si vous ne vérifiez qu'une seule fois @@ ERROR, ne détecterez-vous pas seulement une erreur dans la dernière instruction de votre code SQL? Par exemple. 'SELECT 1/0; SELECT 1; SELECT @@ ERROR' donne une erreur de 0. Utiliser @@ ERROR semble pire que d'utiliser les blocs TRY CATCH. – Jim

2

Voici ce que j'ai utilisé pour réémettre une exception après annulation de la transaction. Cela donne aussi le numéro de ligne de l'erreur.

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

Afin d'éviter la répétition des numéros d'information de procédure/erreur/ligne dans plusieurs scénarios de capture, j'utilise une procédure similaire, avec la légère modification comme suit:

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 

Donc, si nous avons déjà intercepté et re-déclenché l'erreur avec ce SP, nous n'ajoutons pas à plusieurs reprises les informations supplémentaires, donc à l'étendue externe, nous ne voyons que l'erreur telle que re-levée à l'origine.

Dans les exemples affichés ci-dessus, la sortie à double capture serait la même que la sortie à capture unique. J'inclus également le niveau d'imbrication dans le message d'erreur pour faciliter le débogage.

14

Dans SQL 2012, ils ont ajouté le nouveau mot clé throw, qui peut également être utilisé pour re-lancer une exception

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