É possível escolher RAISERROR ou THROW, dependendo da versão do SQL Server?

11

Aqui está o meu código agora:

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

Funciona muito bem, a menos que seja executado em uma máquina com SQL 2008. Gostaria que o bloco CATCH fiz uma verificação na versão SQL e execute THROW se for igual ou superior a 2012, e RAISERROR se for 2008. erros de sintaxe, e eu estou querendo saber se é mesmo possível. Mesmo algo simples como este não está funcionando para mim.

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

Qualquer conselho é apreciado.

thomasjbarrett
fonte

Respostas:

9

Não, isso não é possível.

Essa sintaxe é inválida nas versões anteriores e causará um erro de compilação.

Não é possível ocultar o dentro de THROWum EXECbloco de captura, pois um arremesso sem parâmetros deve estar diretamente contido dentro da captura.

Você precisaria implantar a versão do código desejada de acordo com a versão do SQL Server na qual está implantando (e infelizmente não há um bom suporte para isso nas ferramentas SSDT que eu conheço - não há equivalente a incluir linhas de código seletivamente por meio de compilação condicional)

Martin Smith
fonte
4

Deve-se ressaltar que, mesmo que fosse tecnicamente possível alternar entre THROWe RAISERROR, você (provavelmente) não desejaria realmente fazer isso. Por quê? Como a capacidade muito bacana dos sem parâmetros THROWde repetir o erro usando o mesmo número de mensagem (ou seja, em Msg 8134vez de Msg Xonde X> = 50000) não é a única diferença entre eles: THROWé abortar lotes enquanto RAISERRORnão é. Essa pode ser uma diferença comportamental importante, conforme demonstrado abaixo.

Configuração de teste

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

Teste 1

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

Devoluções:

"Results" Tab:

DivideByZero
{empty result set}

"Messages" Tab:

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

Teste 2

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

Devoluções:

"Results" Tab:

DivideByZero
{empty result set}

BB
2

DD
4

"Messages" Tab:

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

Para ser justo, é possível mascarar essa diferença, fazendo o seguinte:

  • Sempre agrupe todas as chamadas ao código usando THROWdentro de uma TRY...CATCHconstrução (demonstrada abaixo)
  • Nunca coloque o código após o THROW(bem, exceto END CATCH;)

Teste 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

Devoluções:

"Results" Tab:

DivideByZero
{empty result set}

ErrorNumber     ErrorMessage
8134            Divide by zero error encountered.

FF
6

Teste 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

Devoluções:

"Results" Tab:

DivideByZero
{empty result set}

ErrorNumber     ErrorMessage
50000           test, yo!

HH
8
Solomon Rutzky
fonte
3

Acredito que a resposta de Martin Smith esteja quase 100% correta.

A única maneira de fazer isso é com o SQL dinâmico, e você teria que duplicar uma grande quantidade de seu código envolvendo todos os seus blocos try / catch (ou toda a instrução de procedimento de criação, se quiser ter duas versões de todos os aqueles) que são executados dependendo da versão.

Isso seria um pesadelo para manter. Não faça isso.

Existe uma maneira de executar a instrução SQL com base na versão do SQL Server?

SqlZim
fonte