Czy można wybrać RAISERROR lub THROW w zależności od wersji SQL Server?


11

Oto mój kod w tej chwili:

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

Działa świetnie, chyba że jest uruchamiany na komputerze z SQL 2008. Chciałbym, aby blok CATCH sprawdził wersję SQL i uruchomił THROW, jeśli jest równy lub wyższy niż 2012, i RAISERROR, jeśli jest 2008. Ciągle się uruchamiam błędy składniowe i zastanawiam się, czy to w ogóle możliwe. Nawet coś takiego prostego nie działa dla mnie.

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

Wszelkie porady są mile widziane.

Odpowiedzi:


9

Nie, to nie jest możliwe.

Jest to nieprawidłowa składnia we wcześniejszych wersjach i spowoduje błąd kompilacji.

Nie jest możliwe ukrycie się THROWw EXECwewnętrznym bloku zaczepu, ponieważ rzut bezparametrowy musi być zawarty bezpośrednio w zaczepie.

Będziesz musiał wdrożyć żądaną wersję kodu zgodnie z wersją SQL Server, na której wdrażasz (i niestety nie ma dobrego wsparcia w tym narzędziu SSDT, o którym wiem - nie ma odpowiednika włączania wierszy kodu selektywnie przez kompilacja warunkowa)


4

Należy zauważyć, że nawet gdyby technicznie możliwe było przełączanie między nimi, THROWa RAISERRORTy (najprawdopodobniej) nie chciałbyś tego robić. Dlaczego? Ponieważ bardzo sprytna zdolność bezparametrowego THROWponownego rzucenia błędu przy użyciu tego samego numeru komunikatu (tj. Msg 8134Zamiast Msg Xgdzie X> = 50000) nie jest jedyną różnicą między nimi: THROWjest przerywanie wsadowe, podczas gdy RAISERRORnie jest. Może to być istotna różnica w zachowaniu, jak pokazano poniżej.

Konfiguracja testowa

--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];

Zwroty:

"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];

Zwroty:

"Results" Tab:

DivideByZero
{empty result set}

BB
2

DD
4

"Messages" Tab:

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

Aby być uczciwym, można maskować tę różnicę, wykonując następujące czynności:

  • Zawsze zawijaj wszystkie wywołania kodu za pomocą THROWwewnątrz TRY...CATCHkonstrukcji (pokazano poniżej)
  • Nigdy nie umieszczaj kodu po THROW(no, oprócz 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

Zwroty:

"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

Zwroty:

"Results" Tab:

DivideByZero
{empty result set}

ErrorNumber     ErrorMessage
50000           test, yo!

HH
8

3

Uważam, że odpowiedź Martina Smitha jest prawie w 100% słuszna.

Jedynym sposobem, aby to zrobić, jest użycie dynamicznego SQL-a i musiałbyś zduplikować dużą ilość kodu, pakując wszystkie bloki try / catch (lub całą instrukcję tworzenia procedury, jeśli masz mieć dwie wersje wszystkich te), które działają w zależności od wersji.

To byłby koszmar do utrzymania. Nie rób tego

Czy istnieje sposób wykonania instrukcji SQL na podstawie wersji SQL Server?

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.