Можно ли выбрать 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

Любой совет приветствуется.

thomasjbarrett
источник

Ответы:

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, и вам придется дублировать огромный объем кода, оборачивая все блоки try / catch (или весь оператор создания процедуры, если у вас будет две версии всех те), которые выполняются в зависимости от версии.

Это было бы кошмаром для поддержания. Не делай этого.

Есть ли способ выполнить оператор SQL на основе версии SQL Server?

SqlZim
источник