Introduction
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
- Starting with error handling.
- Raising, throwing, and customizing your errors.
- 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
Violation of UNIQUE KEY constraint 'unique_product_name'.
Cannot insert duplicate key in object 'dbo.products'.
The duplicate key value is (Trek Powerfly 5 - 2018).
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
|
|
Msg 2627, Level 14, State 1, Line 1
Violation of UNIQUE KEY constraint 'unique_name'.
Cannot insert duplicate key in object 'dbo.products'.
The duplicate key value is (Trek Powerfly 5 - 2018).
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 from1 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 errorThe fourth value is
Line
-> give you the line number.
Finally, if the error happens within a stored procedureor a
trigger`, 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?
Msg 207, Level 16, State 1, Line 2
Invalid column name 'non_existent_column'.
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
Msg 2627, Level 14, State 1, Line 1
Violation of UNIQUE KEY constraint 'unique_name'.
Cannot insert duplicate key in object 'dbo.products'.
The duplicate key value is (Trek Powerfly 5 - 2018).
this is the original 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 errorERROR_LINE()
returns the number of the line errorERROR_PROCEDURE()
returns the name of the stored proc/trigger, Null if there is no stored pro/trigError_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
|
|
Msg. 50000, Level 16, State 1, Line 3
No staff member with such an id
If we don’t specify an error number, the error number will always be 50000
Let’s change the message text with placeholders
|
|
Msg. 50000, Level 16, State 1, Line 3
No staff member with id 15
It’s recommended to look at Microsoft documentation for more information
RAISERROR with error number
|
|
Msg. 60000, Level 16, State 1, Line 1
This is a test message
This error number comes from sys.messages
RIASERROR - Example with Try .. Catch
|
|
No staff member with such id
Msg. 50000, Level 9, State 1
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
(0 rows affected)
Msg. 8134, Level 16, State 1, Line 2
Divide by zero error encountered.
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
- Variable by concatenating strings
FORMATEMESSAGE()
function
Using a variable and the CONCAT function
|
|
Msg. 50000, Level 16, State 1, Line 5
There is no staff member for id 500. Try With another one.
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
|
|