Is it possible to choose RAISERROR or THROW depending on SQL Server version?
No this isn't possible.
This is invalid syntax in earlier versions and will cause a compile error.
It is not possible to hide the THROW
in an EXEC
inside the catch block either as a parameterless throw must be directly contained inside the catch.
You would need to deploy the code version you want according to the version of SQL Server you are deploying to (and unfortunately there is not good support for this either in the SSDT tooling that I am aware of - no equivalent of including code lines selectively through conditional compilation)
It should be pointed out that, even if it were technically possible to alternate between THROW
and RAISERROR
, you (most likely) wouldn't want to actually do this. Why? Because the very nifty ability of the parameterless THROW
to re-throw the error using the same Message Number (i.e. Msg 8134
instead of Msg X
where X
>= 50000) isn't the only difference between them: THROW
is batch-aborting while RAISERROR
is not. This can be an important behavioral difference as demonstrated below.
Test Setup
--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];
Returns:
"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];
Returns:
"Results" Tab:
DivideByZero
{empty result set}
BB
2
DD
4
"Messages" Tab:
Msg 50000, Level 16, State 1, Procedure ##RaisError, Line 45
test, yo!
To be fair, it is possible to mask this difference by doing the following:
- Always wrap all calls to code using
THROW
within aTRY...CATCH
construct (demonstrated below) - Never place code after the
THROW
(well, except forEND 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
Returns:
"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
Returns:
"Results" Tab:
DivideByZero
{empty result set}
ErrorNumber ErrorMessage
50000 test, yo!
HH
8
I believe Martin Smith's answer is almost 100% right.
The only way to do this is with dynamic SQL, and you would have to duplicate a huge amount of your code by wrapping all of your try/catch blocks (or the whole create procedure statement if you are going to have two version of all of those) that execute depending on the version.
That would be a nightmare to maintain. Don't do it.
Is there a way to execute SQL statement based on SQL Server version?