Вывод ошибок mssql

This article explores the SQL Server PRINT statements, and its alternative SQL Server RAISEERROR statements to print
messages in a query.

Introduction

Suppose you execute a query with multiple steps. We want to get a message once each step completes. It helps to track the query progress. Usually, we use the SQL PRINT statement to print corresponding messages or track the variable values while query progress.

We also use interactions or multiple loops in a query with a while or for a loop. We can also use the SQL PRINT
statement to track the iteration.

We use the SQL Server PRINT statement to return messages to the client. We specify the message as string expressions input. SQL Server returns the message to the application.

In this article, we will explore several use cases of SQL PRINT statement, its limitations, and alternatives of SQL
PRINT statements.

Example 1: SQL Server PRINT statement to print a string

It is the simplest example. Execute the following query in SSMS, and it returns the following message in the output:

PRINT ‘My Name is Rajendra Gupta’;

PRINT statement to print a string

In SSMS, we get the PRINT statement output in SSMS message window as shown in the above image.

Example 2: PRINT statement to print a variable value

We can use the SQL PRINT statement to print a variable value as well. Let’s define a message in a variable and later
print this message:

DECLARE @Msg VARCHAR(300)= ‘My Name is Rajendra Gupta’;

PRINT @Msg;

PRINT statement to print a variable value

Example 3: SQL Server PRINT statement to print an integer value

We can use the SQL PRINT statement to print an integer value, as shown below:

DECLARE @a INT = 1000

PRINT @a

PRINT statement to print an integer value

We can specify only CHAR, NCHAR, VARCHAR or NVARCHAR data types in the PRINT statement. In this case, it implicitly converts an integer value to the VARCHAR data type internally.

Let’s use another example with an integer variable and a string in the PRINT statement. You get an error message in
data type conversion because SQL Server is trying to convert the varchar data type to integer. In data type
precedence, the integer data type has high precedence than the varchar data type:

DECLARE @a INT = 1000

PRINT ‘Your queue no is ‘ + @a

Conversion failure message

We explicitly convert the integer data type to varchar using the SQL CAST statement:

DECLARE @a INT= 1000;

PRINT ‘Your queue no is ‘ + CAST(@a AS VARCHAR(10));

explicitly convert data type

We can use the SQL CONCAT function as well, and it automatically does data type conversion for us. In the following
query, we get the output using the CONCAT function similar to the CAST operator:

DECLARE @a INT= 1000;

PRINT CONCAT(‘Your queue no is : ‘,@a)

SQL CONCAT

Example 4: SQL Server PRINT statement with XML type variable value

We can use XML data type as well with the PRINT statement, but it requires data conversion.

As shown in the following output, we cannot directly use an XML variable in the PRINT statement. It gives an error
message that implicit conversion from XML to nvarchar is not allowed:

DECLARE @a XML = ‘<CustomerID=»1″ CustomerName=»Rajendra»/>’

PRINT @a

PRINT statement with XML type variable value

We can use SQL CAST or CONVERT function explicitly and get the required output:

@a XML = ‘<Customer id=»1″ Name=»Rajendra»/>’

PRINT CAST(@a AS VARCHAR(100))

PRINT statement with XML type variable value

Example 5: SQL Server PRINT Statement with IF conditions

Let’s use the PRINT statement to print the message satisfied in the IF condition. In this example, the variable @a
contains a string. The IF condition checks for the string and prints message satisfying the condition:

DECLARE @a VARCHAR(100)= ‘Mango’;

IF @a = ‘Mango’

    PRINT N‘It is a Fruit’;

    ELSE

    PRINT N‘It is a vegetable’;  

GO

PRINT Statement with IF conditions

Example 6: PRINT Statement with NULL values

We cannot print NULL in the message using the SQL PRINT statement. The following query does not return any result:

 PRINT Statement with NULL values

Let’s use the following query that contains a variable with NULL values. In the PRINT statement, we use a string
along with this variable, and it does not return any message. The concatenation of a string and the variable @a
(NULL) that does not return any output:

DECLARE @a NVarChar(100)= NULL

PRINT ‘Hello’ + @a

 PRINT Statement with NULL values

Example 7: SQL Server PRINT Statement in a WHILE loop

As stated earlier, many times, we require knowing information about each iteration when query running in a loop such as WHILE or FOR.

The following query uses WHILE loop and prints a message about each iteration:

DECLARE @a INT;

SET @a = 1;

WHILE(@a < 10)

    BEGIN

        PRINT CONCAT(‘This is Iteration no:’ , @a)

        SET @a  = @a + 1;

          END;

It gives the following output. We can use a Print statement with an appropriate message and track query progress.

PRINT Statement in a WHILE loop

Limitations of SQL Server PRINT statement

In the previous example, we saw the use of cases of SQL PRINT statements. We have the following limitations with
PRINT as well:

  1. We need to use CAST, CONVERT or CONCAT functions in the PRINT statement with variables
  2. We cannot see a PRINT statement in SQL PROFILER
  3. The PRINT statement does not return the message immediately; it buffers the output and displays them

Let’s elaborate point no 3 and see its alternatives.

Execute the following query that contains two PRINT statements. Looking at the query, you might think of output in
the following form:

  • It gives the message from the first PRINT statement
  • Waits for 5 seconds
  • It gives the message for a second PRINT statement
  • Waits for another 5 seconds

PRINT ‘My Name is Rajendra Gupta’;

WAITFOR DELAY ’00:00:05′;

PRINT ‘You are reading article on SQL PRINT statement’;

WAITFOR DELAY ’00:00:05′;

In the following GIF image, you can note that it prints message from both SQL PRINT statements together after 10
seconds:

PRINT Statement in a WHILE loop

Let’s use another example and see PRINT statement behavior. In this, we want to print the message as soon as
iteration completes:

DECLARE @a INT;

SET @a = 1;

WHILE(@a < 15)

    BEGIN

        PRINT CONCAT(‘This is Iteration no:’ , @a)

              WAITFOR DELAY ’00:00:01′

        SET @a  = @a + 1;

          END;

In the output, we can note that all messages output from PRINT statements appear together once the execution
completes. The loop executes 14 times and waits 1 second on each execution. Therefore, we get output after 14
seconds for all PRINT statements:

PRINT Statement in a WHILE loop

SQL Server buffers messages for multiple PRINT statements and displays them together.

It is not the desired output, and in any case, it might not be useful as you cannot track query progress in
real-time.

SQL Server RAISERROR statement

We can use an alternative to the SQL PRINT statement that is RAISERROR.

We require a few arguments in RAISERROR statements.

  • Message – It is the message that we want to print
  • Severity – It is a number between 0 and 25 and defines the severity of the messages. It treats
    the message differently with each severity. We will see a few examples of it in this article
  • State – It is a number between 0 and 255 to distinguish one error from another. It is good to
    use value 1 for this article

We need to use RAISERROR statement with NOWAIT clause; otherwise, it shows the same behavior as of SQL PRINT
statement:

RAISERROR(‘My Name is Rajendra Gupta’, 0, 1) WITH NOWAIT;

WAITFOR DELAY ’00:00:05′;

RAISERROR(‘You are reading article on SQL PRINT statement’, 0, 1) WITH NOWAIT;

WAITFOR DELAY ’00:00:05′;

In the following output with SQL Server RAISERROR statements, we can note the following:

  • It gives the message from the first PRINT statement
  • Waits for 5 seconds
  • It gives the message for the second PRINT statement
  • Waits for another 5 seconds

PRINT Statement in a WHILE loop

Previously we used severity 0 in the RAISERROR statement. Let’s use the same query with severity 16:

RAISERROR(‘My Name is Rajendra Gupta’, 16, 1) WITH NOWAIT;

WAITFOR DELAY ’00:00:05′;

RAISERROR(‘You are reading article on SQL PRINT statement’, 16, 1) WITH NOWAIT;

WAITFOR DELAY ’00:00:05′;

In the output, we can see the message appears in red, and it shows the message as an error instead of a regular message. You also get message id, level, and state as well:

Severity 16 in SQL Server RAISERROR

Let’s execute the following query with severity 1 and severity 16. Severity 1 shows the message with additional information, but it does not show the message as an error. You can see the text color in black.

Another SQL Server RAISERROR shows the output message as an error:

RAISERROR(‘My Name is Rajendra Gupta’, 1, 1) WITH NOWAIT;

WAITFOR DELAY ’00:00:05′;

RAISERROR(‘You are reading article on SQL PRINT statement’, 16, 1) WITH NOWAIT;

WAITFOR DELAY ’00:00:05′;

Severity 16 and 1 in RAISERROR

We cannot use SQL Server RAISERROR directly using the variables. We get the following output that is not the desired
output:

DECLARE @a INT;

SET @a = 1;

WHILE(@a < 15)

    BEGIN

        RAISERROR(‘This is Iteration no:’, @A, 0, 1) WITH NOWAIT;

        SET @a = @a + 1;

    END;

It prints the message but does not show the value of the variable:

variable with RAISERROR

We need to use the C-style print statements with RAISERROR. The following query shows the variable with the
RAISERROR. You can notice that we use %s and %d to print a string and integer value:

DECLARE @a INT;

SET @a = 1;

DECLARE @S VARCHAR(100)= ‘This is iteration no’;

WHILE(@a < 5)

    BEGIN

        RAISERROR(‘%s:%d’, 0, 1, @s, @a) WITH NOWAIT;

        SET @a = @a + 1;

    END;

We get the instant output in SQL Server RAISERROR along with WITH NOWAIT statement as per our requirement and does
not use buffer to display output once the query finishes:

PRINT Statement in a WHILE loop

You might confuse between RAISERROR statement that it is for raising error messages in SQL Server. We can use it as an alternative to the SQL PRINT statement as well. Usually, developers use PRINT statements only to gives messages in a query. You should explore RAISERROR statements for your queries, stored procedures.

Conclusion

In this article, we explored the SQL Server PRINT statement and its usages to track useful milestones in a query. We also learned the limitations of it along with alternative solution RAISERROR statement in SQL Server. SQL Server
RAISERROR gives you a great advantage to control output buffer behavior.

  • Author
  • Recent Posts

Rajendra Gupta

Hi! I am Rajendra Gupta, Database Specialist and Architect, helping organizations implement Microsoft SQL Server, Azure, Couchbase, AWS solutions fast and efficiently, fix related issues, and Performance Tuning with over 14 years of experience.

I am the author of the book «DP-300 Administering Relational Database on Microsoft Azure». I published more than 650 technical articles on MSSQLTips, SQLShack, Quest, CodingSight, and SeveralNines.

I am the creator of one of the biggest free online collections of articles on a single topic, with his 50-part series on SQL Server Always On Availability Groups.

Based on my contribution to the SQL Server community, I have been recognized as the prestigious Best Author of the Year continuously in 2019, 2020, and 2021 (2nd Rank) at SQLShack and the MSSQLTIPS champions award in 2020.

Personal Blog: https://www.dbblogger.com
I am always interested in new challenges so if you need consulting help, reach me at rajendra.gupta16@gmail.com

View all posts by Rajendra Gupta

Rajendra Gupta

message_text — сообщение, которое вы хотите показать при ошибке. Замечание: вы можете добавлять пользовательские сообщения для вывода информации об ошибке. Смотрите следующий раздел статьи.
message_id — id сообщения об ошибке. Если вы хотите вывести пользовательское сообщение, вы можете определить этот идентификатор. Посмотрите список идентификаторов сообщений в sys.messages DMV.
Запрос:

select * from sys.messages

Вывод:

severity — серьезность ошибки. Тип данных переменной severity — smallint, значения находятся в диапазоне от 0 до 25. Допустимыми значениями серьезности ошибки являются:

  • 0-10
  • — информационные сообщения

  • 11-18
  • — ошибки

  • 19-25
  • — фатальные ошибки

Замечание: Если вы создаете пользовательское сообщение, сложность, указанная в этом сообщении, будет перебиваться сложностью, заданной в операторе RAISERROR.
state — уникальное идентификационное число, которое может использоваться для раздела кода, вызывающего ошибку. Тип данных параметра state — smallint, и допустимые значения между 0 и 255.

Теперь давайте перейдем к практическим примерам.

Пример 1: использование оператора SQL Server RAISERROR для вывода сообщения

В этом примере вы можете увидеть, как можно отобразить ошибку или информационное сообщение с помощью оператора RAISERROR.

Предположим, что вы хотите отобразить сообщение после вставки записей в таблицу. Мы можем использовать операторы PRINT или RAISERROR. Ниже — код:

SET nocount ON 
INSERT INTO tblpatients
(patient_id,
patient_name,
address,
city)
VALUES ('OPD00006',
'Nimesh Upadhyay',
'AB-14, Ratnedeep Flats',
'Mehsana')
RAISERROR ( 'Patient detail added successfully',1,1)

Вывод:

Как видно на рисунке выше, ID сообщения равно 50000, поскольку это пользовательское сообщение.

Пример 2: оператор SQL RAISERROR с текстом динамического сообщения

Теперь посмотрите, как мы можем создать текст динамического сообщения для оператора SQL RAISERROR.

Предположим, что мы хотим напечатать в сообщении ID пациента. Я описал локальную переменную с именем @PatientID, которая содержит patient_id. Чтобы отобразить значение переменной @PatientID в тексте сообщения, мы можем использовать следующий код:

DECLARE @PatientID VARCHAR(15) 
DECLARE @message NVARCHAR(max)
SET @PatientID='OPD00007'
SET @message ='Patient detail added successfully. The OPDID is %s'
INSERT INTO tblpatients
(patient_id,
patient_name,
address,
city)
VALUES ('' + @PatientID + '',
'Nimesh Upadhyay',
'AB-14, Ratnedeep Flats',
'Mehsana')
RAISERROR ( @message,1,1,@patientID)

Вывод:

Для отображения строки в операторе RAISERROR, мы должны использовать операторы print в стиле языка Си.

Как видно на изображении выше, для вывода параметра в тексте сообщения я использую опцию %s, которая отображает строковое значение параметра. Если вы хотите вывести целочисленный параметр, вы можете использовать опцию %d.

Использование SQL RAISERROR в блоке TRY..CATCH

В этом примере мы добавляем SQL RAISERROR в блок TRY. При запуске этого кода он выполняет связанный блок CATCH. В блоке CATCH мы будем выводить подробную информацию о возникшей ошибке.

BEGIN try 
RAISERROR ('Error invoked in the TRY code block.',16,1 );
END try
BEGIN catch
DECLARE @ErrorMsg NVARCHAR(4000);
DECLARE @ErrSeverity INT;
DECLARE @ErrState INT;
SELECT @ErrorMsg = Error_message(),
@ErrSeverity = Error_severity(),
@ErrState = Error_state();
RAISERROR (@ErrorMsg,
@ErrSeverity,
@ErrState
);
END catch;

Так мы добавили оператор RAISERROR с ВАЖНОСТЬЮ МЕЖДУ 11 И 19. Это вызывает выполнение блока CATCH.

В блоке CATCH мы показываем информацию об исходной ошибке, используя оператор RAISERROR.
Вывод:

Как вы можете увидеть, код вернул информацию об исходной ошибке.

Теперь давайте разберемся, как добавить пользовательское сообщение, используя хранимую процедуру sp_addmessage.

Хранимая процедура sp_addmessage

Мы можем добавить пользовательское сообщение, выполнив хранимую процедуру sp_addmessages. Синтаксис процедуры:

EXEC Sp_addmessage 
@msgnum= 70001,
@severity=16,
@msgtext='Please enter the numeric value',
@lang=NULL,
@with_log='TRUE',
@replace='Replace';

@msgnum: задает номер сообщения. Тип данных параметра — integer. Это ID пользовательского сообщения.
@severity: указывает уровень серьезности ошибки. Допустимые значения от 1 до 25. Тип данных параметра — smallint.
@messagetext: задает текст сообщения, который вы хотите выводить. Тип данных параметра nvarchar(255), значение по умолчанию NULL.
@lang: задает язык, который вы хотите использовать для вывода сообщения об ошибке. Значение по умолчанию NULL.
@with_log: этот параметр используется для записи сообщения в просмотрщик событий. Допустимые значения TRUE и FALSE. Если вы задаете TRUE, сообщение об ошибке будет записано в просмотрщик событий Windows. Если выбрать FALSE, ошибка не будет записана в журнал ошибок Windows.
@replace: если вы хотите заменить существующее сообщение об ошибке на пользовательское сообщение и уровень серьезности, вы можете указать это в хранимой процедуре.

Предположим, что вы хотите создать сообщение об ошибке, которое возвращает ошибку о недопустимом качестве (invalid quality). В операторе INSERT значение invalid_quality находится в диапазоне между 20 и 100. Сообщение следует рассматривать как ошибку с уровнем серьезности 16.

Чтобы создать такое сообщение, выполните следующий запрос:

USE master;
go
EXEC Sp_addmessage
70001,
16,
N'Product Quantity must be between 20 and 100.';
go

После добавления сообщения выполните запрос ниже, чтобы увидеть его:

USE master 
go
SELECT * FROM sys.messages WHERE message_id = 70001

Вывод:

Как использовать пользовательские сообщения об ошибках

Как упоминалось выше, мы должны использовать message_id в операторе RAISERROR для пользовательских сообщений.

Мы создали сообщение с ID = 70001. Оператор RAISERROR должен быть таким:

USE master 
go
RAISERROR (70001,16,1 );
go

Вывод:

Оператор RAISERROR вернул пользовательское сообщение.

Хранимая процедура sp_dropmessage

Хранимая процедура sp_dropmessage используется для удаления пользовательских сообщений. Синтаксис оператора:

EXEC Sp_dropmessage @msgnum

Здесь @msgnum задает ID сообщения, которое вы хотите удалить.

Теперь мы хотим удалить сообщение, с ID = 70001. Запрос:

EXEC Sp_dropmessage 70001

Выполним следующий запрос для просмотра сообщения после его удаления:

USE master 
go
SELECT * FROM sys.messages WHERE message_id = 70001

Вывод:

Как видно, сообщение было удалено.

Transact-SQL provides facilities to raise errors and warnings, as well as handling them. Here’s an introduction to this subject.

Raising errors and warnings

In SQL Server, we can generate an error or a warning with the RAISERROR statement. The most common usage looks like this:

RAISERROR (
    'Invalid user id',
    20, -- severity
    0   -- state
);

In this example, we’re raising a fatal error with severity 25 and state 0.

Severity must be in the range between 0 and 19. 0-18 represents a warning, 19-25 represents an error.

State must be in the range between 0 and 255. has no effect on SQL Server itself, but it’s useful for handling the error programmatically. If an error with the same message and severity can be raised in several places, the state can be used to find out where the error was raised.

Severity and state may be adjusted by SQL Server. If they’re less then 0 the value will be 0. If they exceed the maximum value, the maximum value will be used.

An error is a fatal error that terminates the current operation, and a warning is an anomaly that could indicate a problem or not, depending on the situation. Let’s see the differences, and how to generate them.

Errors

A mentioned, an error has a severity from 19 to 25.

An error must be written into the logs, so the WITH LOG option is required, as follows:

RAISERROR (...) WITH LOG;

An error is considered fatal, so it has some effects:

  • It rolls back the current transaction;
  • It terminates the execution of the current T-SQL program (a trigger, a procedure, etc).

Warnings

As mentioned, a warning must have a severity from 0 to 18. Warnings can only be handled by a TRY ... CATCH statement if their severity is bigger than 10.

The WITH LOG option discussed above is optional for warnings.

A warning does not rollback the transaction and does not terminate a program execution. We can, however, do one of these things or both:

ROLLBACK;
RAISERROR (...);
RETURN 1;

The order of the statements is important here:

  • ROLLBACK must precede RAISERROR, because if the warning is handled by a TRY ... CATCH block, anything after RAISERROR won’t be executed.
  • RETURN should follow both ROLLBACK and RAISERROR, or they won’t be executed.

RETURN always returns an integer value, 0 by default. This value can be used to indicate if the procedure succeeded or not, and why it failed. Conventionally, 0 indicates success and any other value indicates failure. We can use different non-zero returned values to indicate different problems: for example 1 for wrong arguments, 2 for missing rows, etc.

Other options

RAISERROR has a few more options that can occasionally be useful.

NOWAIT

Errors are normally sent to the client (and shown to the user) when the current program or T-SQL statement ends. To send the error to the client immediately, specify WITH NOWAIT:

RAISERROR (...) WITH NOWAIT;

SETERROR

You can also use SETERROR to set the error number to 50000, regardless of the severity level. 50000 is the code reserver to general user defined errors, by convention.

Reference

More articles:

  • SQL Server: Handle errors

Summary: in this tutorial, you will learn how to use the SQL Server RAISERROR statement to generate user-defined error messages.

If you develop a new application, you should use the THROW statement instead.

SQL Server RAISEERROR statement overview

The RAISERROR statement allows you to generate your own error messages and return these messages back to the application using the same format as a system error or warning message generated by SQL Server Database Engine. In addition, the RAISERROR statement allows you to set a specific message id, level of severity, and state for the error messages.

The following illustrates the syntax of the RAISERROR statement:

RAISERROR ( { message_id | message_text | @local_variable }  
    { ,severity ,state }  
    [ ,argument [ ,...n ] ] )  
    [ WITH option [ ,...n ] ];
Code language: SQL (Structured Query Language) (sql)

Let’s examine the syntax of the RAISERROR for better understanding.

message_id

The message_id is a user-defined error message number stored in the sys.messages catalog view.

To add a new user-defined error message number, you use the stored procedure sp_addmessage. A user-defined error message number should be greater than 50,000. By default, the RAISERROR statement uses the message_id 50,000 for raising an error.

The following statement adds a custom error message to the sys.messages view:

EXEC sp_addmessage 
    @msgnum = 50005, 
    @severity = 1, 
    @msgtext = 'A custom error message';
Code language: SQL (Structured Query Language) (sql)

To verify the insert, you use the following query:

SELECT    
    *
FROM    
    sys.messages
WHERE 
    message_id = 50005;
Code language: SQL (Structured Query Language) (sql)

To use this message_id, you execute the RAISEERROR statement as follows:

RAISERROR ( 50005,1,1)
Code language: SQL (Structured Query Language) (sql)

Here is the output:

A custom error message
Msg 50005, Level 1, State 1
Code language: SQL (Structured Query Language) (sql)

To remove a message from the sys.messages, you use the stored procedure sp_dropmessage. For example, the following statement deletes the message id 50005:

EXEC sp_dropmessage 
    @msgnum = 50005;  
Code language: SQL (Structured Query Language) (sql)

message_text

The message_text is a user-defined message with formatting like the printf function in C standard library. The message_text can be up to 2,047 characters, 3 last characters are reserved for ellipsis (…). If the message_text contains 2048 or more, it will be truncated and is padded with an ellipsis.

When you specify the message_text, the RAISERROR statement uses message_id 50000 to raise the error message.

The following example uses the RAISERROR statement to raise an error with a message text:

RAISERROR ( 'Whoops, an error occurred.',1,1)
Code language: SQL (Structured Query Language) (sql)

The output will look like this:

Whoops, an error occurred.
Msg 50000, Level 1, State 1
Code language: SQL (Structured Query Language) (sql)

severity

The severity level is an integer between 0 and 25, with each level representing the seriousness of the error.

0–10 Informational messages
11–18 Errors
19–25 Fatal errors
Code language: SQL (Structured Query Language) (sql)

state

The state is an integer from 0 through 255. If you raise the same user-defined error at multiple locations, you can use a unique state number for each location to make it easier to find which section of the code is causing the errors. For most implementations, you can use 1.

WITH option

The option can be LOG, NOWAIT, or SETERROR:

  • WITH LOG logs the error in the error log and application log for the instance of the SQL Server Database Engine.
  • WITH NOWAIT sends the error message to the client immediately.
  • WITH SETERROR sets the ERROR_NUMBER and @@ERROR values to message_id or 50000, regardless of the severity level.

SQL Server RAISERROR examples

Let’s take some examples of using the RAISERROR statement to get a better understanding.

A) Using SQL Server RAISERROR with TRY CATCH block example

In this example, we use the RAISERROR inside a TRY block to cause execution to jump to the associated CATCH block. Inside the CATCH block, we use the RAISERROR to return the error information that invoked the CATCH block.

DECLARE 
    @ErrorMessage  NVARCHAR(4000), 
    @ErrorSeverity INT, 
    @ErrorState    INT;

BEGIN TRY
    RAISERROR('Error occurred in the TRY block.', 17, 1);
END TRY
BEGIN CATCH
    SELECT 
        @ErrorMessage = ERROR_MESSAGE(), 
        @ErrorSeverity = ERROR_SEVERITY(), 
        @ErrorState = ERROR_STATE();

    -- return the error inside the CATCH block
    RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState);
END CATCH;
Code language: SQL (Structured Query Language) (sql)

Here is the output:

Msg 50000, Level 17, State 1, Line 16
Error occurred in the TRY block.
Code language: SQL (Structured Query Language) (sql)

B) Using SQL Server RAISERROR statement with a dynamic message text example

The following example shows how to use a local variable to provide the message text for a RAISERROR statement:

DECLARE @MessageText NVARCHAR(100);
SET @MessageText = N'Cannot delete the sales order %s';

RAISERROR(
    @MessageText, -- Message text
    16, -- severity
    1, -- state
    N'2001' -- first argument to the message text
);
Code language: SQL (Structured Query Language) (sql)

The output is as follows:

Msg 50000, Level 16, State 1, Line 5
Cannot delete the sales order 2001
Code language: SQL (Structured Query Language) (sql)

When to use RAISERROR statement

You use the RAISERROR statement in the following scenarios:

  • Troubleshoot Transact-SQL code.
  • Return messages that contain variable text.
  • Examine the values of data.
  • Cause the execution to jump from a TRY block to the associated CATCH block.
  • Return error information from the CATCH block to the callers, either calling batch or application.

In this tutorial, you will learn how to use the SQL Server RAISERROR statement to generate user-defined error messages.

I’m writing a user-defined function in SQL Server 2008. I know that functions cannot raise errors in the usual way — if you try to include the RAISERROR statement SQL returns:

Msg 443, Level 16, State 14, Procedure ..., Line ...
Invalid use of a side-effecting operator 'RAISERROR' within a function.

But the fact is, the function takes some input, which may be invalid and, if it is, there is no meaningful value the function can return. What do I do then?

I could, of course, return NULL, but it would be difficult for any developer using the function to troubleshoot this. I could also cause a division by zero or something like that — this would generate an error message, but a misleading one. Is there any way I can have my own error message reported somehow?

marc_s's user avatar

marc_s

734k176 gold badges1332 silver badges1460 bronze badges

asked Sep 28, 2009 at 1:33

EMP's user avatar

You can use CAST to throw meaningful error:

create function dbo.throwError()
returns nvarchar(max)
as
begin
    return cast('Error happened here.' as int);
end

Then Sql Server will show some help information:

Msg 245, Level 16, State 1, Line 1
Conversion failed when converting the varchar value 'Error happened here.' to data type int.

answered Jan 13, 2011 at 15:33

Vladimir Korolev's user avatar

Vladimir KorolevVladimir Korolev

2,9871 gold badge17 silver badges10 bronze badges

8

The usual trick is to force a divide by 0. This will raise an error and interrupt the current statement that is evaluating the function. If the developer or support person knows about this behavior, investigating and troubleshooting the problem is fairly easy as the division by 0 error is understood as a symptom of a different, unrelated problem.

As bad as this looks from any point of view, unfortunately the design of SQL functions at the moment allows no better choice. Using RAISERROR should absolutely be allowed in functions.

answered Sep 28, 2009 at 6:02

Remus Rusanu's user avatar

Remus RusanuRemus Rusanu

289k40 gold badges443 silver badges569 bronze badges

Following on from Vladimir Korolev’s answer, the idiom to conditionally throw an error is

CREATE FUNCTION [dbo].[Throw]
(
    @error NVARCHAR(MAX)
)
RETURNS BIT
AS
BEGIN
    RETURN CAST(@error AS INT)
END
GO

DECLARE @error NVARCHAR(MAX)
DECLARE @bit BIT

IF `error condition` SET @error = 'My Error'
ELSE SET @error = '0'

SET @bit = [dbo].[Throw](@error)    

answered Mar 6, 2012 at 12:16

satnhak's user avatar

satnhaksatnhak

9,4375 gold badges64 silver badges81 bronze badges

I think the cleanest way is to just accept that the function can return NULL if invalid arguments are passed. As long is this is clearly documented then this should be okay?

-- =============================================
-- Author: AM
-- Create date: 03/02/2010
-- Description: Returns the appropriate exchange rate
-- based on the input parameters.
-- If the rate cannot be found, returns NULL
-- (RAISEERROR can't be used in UDFs)
-- =============================================
ALTER FUNCTION [dbo].[GetExchangeRate] 
(
    @CurrencyFrom char(3),
    @CurrencyTo char(3),
    @OnDate date
)
RETURNS decimal(18,4)
AS
BEGIN

  DECLARE @ClosingRate as decimal(18,4)

    SELECT TOP 1
        @ClosingRate=ClosingRate
    FROM
        [FactCurrencyRate]
    WHERE
        FromCurrencyCode=@CurrencyFrom AND
        ToCurrencyCode=@CurrencyTo AND
        DateID=dbo.DateToIntegerKey(@OnDate)

    RETURN @ClosingRate 

END
GO

answered Feb 3, 2010 at 10:22

AndyM's user avatar

AndyMAndyM

3,5946 gold badges40 silver badges45 bronze badges

A few folks were asking about raising errors in Table-Valued functions, since you can’t use «RETURN [invalid cast]» sort of things. Assigning the invalid cast to a variable works just as well.

CREATE FUNCTION fn()
RETURNS @T TABLE (Col CHAR)  
AS
BEGIN

DECLARE @i INT = CAST('booooom!' AS INT)  

RETURN

END

This results in:

Msg 245, Level 16, State 1, Line 14
Conversion failed when converting the varchar value ‘booooom!’ to data type int.

Musakkhir Sayyed's user avatar

answered Jun 22, 2016 at 22:53

NightShovel's user avatar

NightShovelNightShovel

3,0321 gold badge31 silver badges35 bronze badges

1

RAISEERROR or @@ERROR are not allowed in UDFs. Can you turn the UDF into a strored procedure?

From Erland Sommarskog’s article Error Handling in SQL Server – a Background:

User-defined functions are usually
invoked as part of a SET, SELECT,
INSERT, UPDATE or DELETE statement.
What I have found is that if an error
appears in a multi-statement
table-valued function or in a scalar
function, the execution of the
function is aborted immediately, and
so is the statement the function is
part of. Execution continues on the
next line, unless the error aborted
the batch. In either case, @@error is
0. Thus, there is no way to detect that an error occurred in a function
from T-SQL.

The problem does not appear with
inline table-functions, since an
inline table-valued function is
basically a macro that the query
processor pastes into the query.

You can also execute scalar functions
with the EXEC statement. In this case,
execution continues if an error occurs
(unless it is a batch-aborting error).
@@error is set, and you can check the
value of @@error within the function.
It can be problematic to communicate
the error to the caller though.

answered Sep 28, 2009 at 1:38

Mitch Wheat's user avatar

Mitch WheatMitch Wheat

296k44 gold badges466 silver badges542 bronze badges

The top answer is generally best, but does not work for inline table valued functions.

MikeTeeVee gave a solution for this in his comment on the top answer, but it required use of an aggregate function like MAX, which did not work well for my circumstance.

I messed around with an alternate solution for the case where you need an inline table valued udf that returns something like select * instead of an aggregate. Sample code solving this particular case is below. As someone has already pointed out… «JEEZ wotta hack» :) I welcome any better solution for this case!

create table foo (
    ID nvarchar(255),
    Data nvarchar(255)
)
go

insert into foo (ID, Data) values ('Green Eggs', 'Ham')
go

create function dbo.GetFoo(@aID nvarchar(255)) returns table as return (
    select *, 0 as CausesError from foo where ID = @aID

    --error checking code is embedded within this union
    --when the ID exists, this second selection is empty due to where clause at end
    --when ID doesn't exist, invalid cast with case statement conditionally causes an error
    --case statement is very hack-y, but this was the only way I could get the code to compile
    --for an inline TVF
    --simpler approaches were caught at compile time by SQL Server
    union

    select top 1 *, case
                        when ((select top 1 ID from foo where ID = @aID) = @aID) then 0
                        else 'Error in GetFoo() - ID "' + IsNull(@aID, 'null') + '" does not exist'
                    end
    from foo where (not exists (select ID from foo where ID = @aID))
)
go

--this does not cause an error
select * from dbo.GetFoo('Green Eggs')
go

--this does cause an error
select * from dbo.GetFoo('Yellow Eggs')
go

drop function dbo.GetFoo
go

drop table foo
go

answered Jun 27, 2013 at 22:29

davec's user avatar

davecdavec

3232 gold badges5 silver badges14 bronze badges

1

I can’t comment under davec’s answer regarding table valued function, but in my humble opinion this is easier solution:

CREATE FUNCTION dbo.ufn_test (@a TINYINT)
RETURNS @returns TABLE(Column1 VARCHAR(10), Value1 TINYINT)
BEGIN
    IF @a>50 -- if @a > 50 - raise an error
    BEGIN
      INSERT INTO @returns (Column1, Value1)
      VALUES('error','@a is bigger than 50!') -- reminder Value1 should be TINYINT
    END

    INSERT INTO @returns (Column1, Value1)
    VALUES('Something',@a)
    RETURN;
END

SELECT Column1, Value1 FROM dbo.ufn_test(1) -- this is okay
SELECT Column1, Value1 FROM dbo.ufn_test(51) -- this will raise an error

answered Jan 14, 2014 at 17:31

Michal Zglinski's user avatar

1

One way (a hack) is to have a function/stored procedure that performs an invalid action. For example, the following pseudo SQL

create procedure throw_error ( in err_msg varchar(255))
begin
insert into tbl_throw_error (id, msg) values (null, err_msg);
insert into tbl_throw_error (id, msg) values (null, err_msg);
end;

Where on the table tbl_throw_error, there is a unique constraint on the column err_msg. A side-effect of this (at least on MySQL), is that the value of err_msg is used as the description of the exception when it gets back up into the application level exception object.

I don’t know if you can do something similar with SQL Server, but worth a shot.

answered Sep 28, 2009 at 1:41

Alex's user avatar

AlexAlex

2,43517 silver badges18 bronze badges

1

Понравилась статья? Поделить с друзьями:

Интересное по теме:

  • Вывод ошибки html
  • Вывод ошибок contact form 7
  • Вывод ошибки pyqt5
  • Вывод ошибки javafx
  • Вывод ошибки golang

  • Добавить комментарий

    ;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: