Чи можливо вибрати RAISERROR або THROW залежно від версії SQL Server?


11

Ось мій код прямо зараз:

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

Чудово працює, якщо це не запуск на машині з SQL 2008. Я хотів би, щоб блок CATCH перевірив версію SQL і запустив THROW, якщо він дорівнює 2012 або вище, і RAISERROR, якщо це 2008 рік. Я продовжую працювати синтаксичні помилки, і мені цікаво, чи це навіть можливо. Навіть щось таке, як це, не працює для мене.

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

Будь-яка порада цінується.

Відповіді:


9

Ні, це неможливо.

Це недійсний синтаксис у попередніх версіях і призведе до помилки компіляції.

Це не можливо , щоб приховати THROWв EXECмежах блоку зловити або як кидок повинен без параметрів безпосередньо міститься всередині улову.

Вам потрібно буде розгорнути кодову версію, яку ви хочете, відповідно до версії SQL Server, до якої ви розгортаєтесь (і, на жаль, не існує хорошої підтримки для цього ні в інструментарії SSDT, про який я знаю - немає еквіваленту, включаючи вибіркові рядки коду через умовна компіляція)


4

Слід зазначити, що навіть якби технічно можна було чергувати, THROWі RAISERRORви (швидше за все) не хотіли б насправді цього робити. Чому? Оскільки дуже витончена здатність паралельного THROWповторного викидання помилки, використовуючи той самий номер повідомлення (тобто Msg 8134замість того, Msg Xде X> = 50000), не є єдиною різницею між ними: THROWце тимчасове переривання, поки RAISERRORйого немає. Це може бути важливою різницею поведінки, як показано нижче.

Налаштування тесту

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

Тест 1

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

Повернення:

"Results" Tab:

DivideByZero
{empty result set}

"Messages" Tab:

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

Тест 2

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

Повернення:

"Results" Tab:

DivideByZero
{empty result set}

BB
2

DD
4

"Messages" Tab:

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

Справедливості, маскувати цю різницю можна, зробивши наступне:

  • Завжди обгортайте всі виклики коду, використовуючи THROWвсередині TRY...CATCHконструкції (продемонстровано нижче)
  • Ніколи не розміщуйте код після THROW(ну, крім END CATCH;)

Тест 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

Повернення:

"Results" Tab:

DivideByZero
{empty result set}

ErrorNumber     ErrorMessage
8134            Divide by zero error encountered.

FF
6

Тест 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

Повернення:

"Results" Tab:

DivideByZero
{empty result set}

ErrorNumber     ErrorMessage
50000           test, yo!

HH
8

3

Я вважаю , що відповідь Мартіна Сміта майже на 100% вірна.

Єдиний спосіб зробити це - за допомогою динамічного SQL, і вам доведеться дублювати величезну кількість вашого коду, загортаючи всі ваші блоки спробу / лову (або весь оператор створення процедури, якщо у вас буде дві версії всіх ті), які виконуються залежно від версії.

Це був би кошмар. Не робіть цього.

Чи є спосіб виконати оператор SQL на основі версії SQL Server?

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.