How to Handle T-SQL Errors Properly [Extensive guide]

Introduction

giphy.gif

As a Backend Software Engineer, you will be working with databases a lot on a regular basis, so you should get comfortable reading SQL errors to properly fix them, so it's an essential skill for you as a developer and troubleshooter as well.

Errors in T-SQL are of many types, based on the error you get, you can decide the optimal solution for it. So In this article, I will show you how to read, handle and even customize your errors.

Table of Contents

  1. Starting with error handling.

  2. Raising, throwing, and customizing your errors.

  3. Resources.

Error Handling

let's begin with an example to demonstrate the concept of errors on SQL server Imagine you have a database consisting of Products, Buyers, Staff, and Orders and you have a unique constraint on the product table (on product_name)

And you tried to insert a product with a name that already exits

🤔 what do you think you will get? Of course an error

we can handle this kind of Error using the Try catch block. The syntax of try-catch on SQL is nearly similar to the ones on programming languages, you begin with try and end with catch, you see it's that simple 😄.

The General syntax

In case of your query inside Try throw an error, then you place your error handling statements with the catch block. If there is no error, the catch block is skipped

An example of a statement that fails

message

An error occurred! You are in the CATCH block

An example of successful Insertion

message

Product inserted correctly!

Nested try..catch

A try block or a catch block can nest another try-catch block. an example of nested try-catch

message

An error occurred inserting the product! You are in the first CATCH block

message

An error occurred inserting the error! You are in the second CATCH block

Exercise

You have a stock attribute on products table and you can't have negative values on this attribute.

query result

An error occurred!

Error Anatomy and uncatchable error

📓note:

Not all errors are catchable

Let's break down the error message, to compose a useful information and know exactly how to handle error based on it's Anatomy

  • The first line is error number -> sql errors from 1 to 49999

You can also create your own starting from 50001

select * from sys.message -> to know the complete log of error numbers

message_id
language_id
severity
...
text

2627

1033

14

..

Violation of %ls constraint '%.*ls'. Cannot insert duplcate...

  • The second value is severity level

  • from 0 - 10: informational messages (warnings)

  • from 11 - 16: errors that can be corrected by the user (constraint violation, etc.)

  • from 17 - 26: other errors (software problems, fatal errors)

you can see the whole list through the docuemntation

  • the third value is the state: it gives you more information about the error 1: if SQL server display an error 0-255: own errors -> to raise your own error

  • The fourth value is Line -> give you the line number.

Finally, if the error happens within a stored procedureor atrigger`, you will receive extra data giving you the name of the stored procedure or the name of the trigger

Uncatchable Errors

the try cath we've learned can't catch every kind of error.

  • Severity lower than 11 (Uncatchable)

  • Severity 11 -> 19 (catchable)

  • Compilation errors: objects and columns that don't exist

the severity of 20 or higher that stopped the connection will not be caught but if it didn't cut the connection, it will be caught

compilation error: object and column that doesn't exist

An example

notice the output?

It doesn't give you the actual error which is you are in the CATCH Block As this is a compilation error, the CATCH block can t handle the error

Giving information about the error

and this is the error returned from the catch block

message

An error occurred! You are in the CATCH block

sometimes the default error the query throws is very useful, and by overriding it using CATCH with an error statement we lose the default, however, can still retrieve it using

Error functions

ERROR_NUMBER() returns the number of the error. ERROR_SEVERITY() returns te error severity (11-19) ERROR_STATE() returns the state of the error ERROR_LINE() returns the number of the line error ERROR_PROCEDURE() returns the name of the stored proc/trigger, Null if there is no stored pro/trig Error_message()

An Example

Error_number
Error_severity
Error_state
Error_procedure
Error_line
Error_message

2627

14

1

NULL

2

Violation of UNIQUE KEY constraint 'unique_name'...

Warning:

We can't use error functions outside the catch block

you will get nulls

we can use it inside nested try and catch, But in this case, you will get the last error that occurred

Raising, throwing and customizing your errors

In this section, we will learn

  • How to raise errors.

  • Re-throw original errors.

  • Create your own defined errors.

Raise errors statements

SQL Server provides two statements to raise errors

  • RAISEERROR

  • THROW (Microsoft recommend using it on new application)

RAISERROR syntax

  • the first parameter can be a message string, a message-id, or a variable that contains the message string.

  • the second Parameter -> severity

  • the third -> state

you can optionally add arguments, like strings or numbers

if the message string has some parameter placeholders such as %s or %d, these arguments will replace them

RAISERROR with message string

If we don't specify an error number, the error number will always be 50000

Let's change the message text with placeholders

It's recommended to look at Microsoft documentation for more information

RAISERROR with error number

This error number comes from sys.messages

RIASERROR - Example with Try .. Catch

As you might've guessed, errors below 11 are not catchable.

if we changed the severity level from 9 to 11, the error will be caught by the catch

message

You are in the CATCH Block

Exercise

Throw statment

Recommended by Microsoft over RAISERROR statement

General syntax

📓note: unlike the RAISERROR statement, the THROW statement allows re-throwing an original error caught by a CATCH block

Throw - Without parameters

The original error caused from the try block is dividing by 0, so the output mesasge is the thrown original error

and SELECT statement inside catch has not been executed;

be careful when writing THROW at the end, you should put a semi-colon before the line

THROW

This line is executed!

SQL Server thinks that the word THROW is an alias for the select statement

Throw - with parameters

This syntax can be included within a CATCH block or outside of it.

An example

But only statements with no parameter should be put on the catch block

message

This is an example

Another example

customizing error messages in THROW statements

⚠️Warning:

throw statement doesn't allow the inclusion of parameters placeholders such as %d or %s but we have a hack around this by

  1. Variable by concatenating strings

  2. FORMATEMESSAGE() function

Using a variable and the CONCAT function

Using FORMATEMESSAGE function

we can include wild cards

notice that the throw statement doesn't allow the specification of the severity, SQL server always sets it to 16

FORMATEMESSAGE() with message number

In SQL server we have a view sys.messages which contains messages with according message_id

You'll get a view

message_id
language_id
severity
is_event_logged
text

101

1033

15

0

Query not allowed in Waitfor

...

...

...

...

...

we can choose any message_id or add a new message to this view to customize our errors. To add a new message to sys.messages, we can execute the sp_addmessage stored procedure with the following parameters

  • msg_id must be greater than 500000

  • language is optional, if you don't specify it, it would be the default language of the session.

we can now use this new message_id on th FORMATEMESSAGE()

notice that the throw statement doesn't allow the specification of the severity, SQL server always sets it to 16

A Detailed Example

Resources

Last updated