Est-il possible de choisir RAISERROR ou THROW selon la version de SQL Server?

11

Voici mon code en ce moment:

BEGIN TRY
INSERT INTO TABLE (F1,F2,F3) 
VALUES ('1','2','3')
END TRY
BEGIN CATCH
;THROW
END CATCH

Fonctionne très bien, à moins qu'il ne soit exécuté sur une machine avec SQL 2008. J'aimerais que le bloc CATCH vérifie la version SQL et exécute THROW s'il est égal ou supérieur à 2012, et RAISERROR si c'est 2008. Je continue de courir dans des erreurs de syntaxe, et je me demande si c'est même possible. Même quelque chose de simple comme ça ne fonctionne pas pour moi.

BEGIN CATCH
IF ((SELECT SERVERPROPERTY('productversion')) >= 11) ;THROW
END CATCH

Tout conseil est apprécié.

thomasjbarrett
la source

Réponses:

9

Non, ce n'est pas possible.

Il s'agit d'une syntaxe non valide dans les versions antérieures et provoquera une erreur de compilation.

Il n'est pas possible non plus de masquer le THROWdans un EXECbloc du catch car un lancer sans paramètre doit être directement contenu dans le catch.

Vous devez déployer la version de code que vous souhaitez en fonction de la version de SQL Server sur laquelle vous déployez (et malheureusement, il n'y a pas de bonne prise en charge pour cela dans les outils SSDT que je connais - pas d'équivalent d'inclure des lignes de code de manière sélective via compilation conditionnelle)

Martin Smith
la source
4

Il convient de souligner que, même s'il était techniquement possible d'alterner entre THROWet RAISERROR, vous (très probablement) ne voudriez pas réellement le faire. Pourquoi? Parce que la capacité très astucieuse des paramètres sans paramètre THROWà renvoyer l'erreur en utilisant le même numéro de message (c'est- Msg 8134à- dire au lieu de Msg XX> = 50000) n'est pas la seule différence entre eux: THROWabandonne par lots alors que ce RAISERRORn'est pas le cas. Cela peut être une différence de comportement importante, comme illustré ci-dessous.

Configuration du test

--DROP PROC ##Throw;
--DROP PROC ##RaisError;

GO
CREATE PROCEDURE ##Throw
AS
SET NOCOUNT ON;
BEGIN TRY
  SELECT 1/0 AS [DivideByZero];
END TRY
BEGIN CATCH
  THROW;
END CATCH;
SELECT 1 AS [AA];
GO

CREATE PROCEDURE ##RaisError
AS
SET NOCOUNT ON;
BEGIN TRY
  SELECT 1/0 AS [DivideByZero];
END TRY
BEGIN CATCH
  RAISERROR('test, yo!', 16, 1);
  -- RETURN; -- typically at end of CATCH block when using RAISERROR
END CATCH;
SELECT 2 AS [BB];
GO

Test 1

EXEC ##Throw;
SELECT 3 AS [CC];

Retour:

"Results" Tab:

DivideByZero
{empty result set}

"Messages" Tab:

Msg 8134, Level 16, State 1, Procedure ##Throw, Line 38
Divide by zero error encountered.

Test 2

EXEC ##RaisError;
SELECT 4 AS [DD];

Retour:

"Results" Tab:

DivideByZero
{empty result set}

BB
2

DD
4

"Messages" Tab:

Msg 50000, Level 16, State 1, Procedure ##RaisError, Line 45
test, yo!

Pour être juste, il est possible de masquer cette différence en procédant comme suit:

  • Toujours encapsuler tous les appels au code à l'aide d' THROWune TRY...CATCHconstruction (illustré ci-dessous)
  • Ne placez jamais de code après le THROW(enfin, sauf pour END CATCH;)

Test 3

BEGIN TRY
  EXEC ##Throw;
  SELECT 5 AS [EE];
END TRY
BEGIN CATCH
  SELECT ERROR_NUMBER() AS [ErrorNumber], ERROR_MESSAGE() AS [ErrorMessage];
END CATCH;
SELECT 6 AS [FF];
GO

Retour:

"Results" Tab:

DivideByZero
{empty result set}

ErrorNumber     ErrorMessage
8134            Divide by zero error encountered.

FF
6

Test 4

BEGIN TRY
  EXEC ##RaisError;
  SELECT 7 AS [GG];
END TRY
BEGIN CATCH
  SELECT ERROR_NUMBER() AS [ErrorNumber], ERROR_MESSAGE() AS [ErrorMessage];
END CATCH;
SELECT 8 AS [HH];
GO

Retour:

"Results" Tab:

DivideByZero
{empty result set}

ErrorNumber     ErrorMessage
50000           test, yo!

HH
8
Solomon Rutzky
la source
3

Je crois que la réponse de Martin Smith est presque à 100% exacte.

La seule façon de le faire est avec SQL dynamique, et vous devrez dupliquer une énorme quantité de votre code en encapsulant tous vos blocs try / catch (ou la totalité de l'instruction create procedure si vous souhaitez avoir deux versions de tous ceux) qui s'exécutent selon la version.

Ce serait un cauchemar à maintenir. Ne le fais pas.

Existe-t-il un moyen d'exécuter une instruction SQL basée sur la version de SQL Server?

SqlZim
la source