Sometimes you’ll need to raise an error in your T-SQL code, like when a check fails and you want to alert the user.
The traditional way is to use the RAISERROR command (Yes, it’s missing an E).

Here’s an example. We’ll set an integer value, and we’ll raise an error if the value is not a positive number.
Running this code will raise an error, since the @StartValue value is -1.

DECLARE @StartValue INT = -1;

SELECT '@StartValue = ' + CAST(@StartValue as VARCHAR(5));

IF (@StartValue <= 0)
	RAISERROR ('@StartValue should be a positive number', 17, 1);

SELECT 'End';

For the RAISERROR call, we pass in three values. The first is the error message that we want to return. The second is the Severity. The links lists what the values mean. Anything 17 or over will cause execution of the query to stop (Although 19 or higher can only be set by members of the sysadmin role). If we use a severity of 11-16, the error message will appear, but execution of the batch will continue.
The third value is state. If the same error message is being raised in multiple places, we can set different state values, so that we know which statement produced the error.
By default, the error number 50000 is returned. Anything 50000 and over is user-defined. We can use the sp_addmessage proc to register an error message with a distinct error number.

In version 2012, the THROW command was added. This is meant to be used as part of a TRY/CATCH block, but we can also use the command on its own.

Here’s the first code example, but using THROW instead of RAISEERROR:

DECLARE @StartValue INT = -1;

SELECT '@StartValue = ' + CAST(@StartValue as VARCHAR(5));

IF (@StartValue <= 0)
	THROW 50001, '@StartValue should be a positive number', 1;

SELECT 'End';

For THROW, we’ll specify the Error Number, the Message and the State. We won’t specify a Severity, the default is 16.

With THROW, we don’t have to register an error number, we can specify that number.

THROW will also rollback any open transactions, as long as we’ve set XACT_ABORT to ON.

We can run this insert and check that table #Test is empty afterwards:

SET XACT_ABORT ON;

DROP TABLE IF EXISTS #Test;

CREATE TABLE #Test ([Value] INT NOT NULL);

BEGIN TRANSACTION

INSERT INTO #Test([Value]) VALUES (1);

THROW 50001, 'Error', 1;

SELECT * FROM #Test;

COMMIT TRANSACTION

RAISEERROR is considered legacy code at this point, Microsoft recommends using THROW. RAISEERROR does support string formatting, where we can add an additional parameter for a placeholder in the error message.