To Be Or Not To Be
Okay, look, the best post about this is, of course, by Erland Sommarskog: Error and Transaction Handling in SQL Server
Just like Erland, it has three parts and three appendices. If you want to learn how to do a whole lot of things right, give yourself a couple days to read through and digest all that.
What I’m here to talk about is some of the stuff you should think about before implementing any error handling at all.
Namely, what do you hope to do with certain errors and conditions?
As far as I’m concerned, there are four outcomes:
- Print an informational message and keep chugging along
- Raise and log the error somewhere and stop everything (maybe rollback, maybe don’t)
- Raise and log the error somewhere and keep going
- Use the type of error to retry the thing you’re trying to do
If you’re not going to do one of those things, there’s no point in even considering error handling (and to a large extent, transactions) in your procedures.
Let’s talk through some examples of each one.
Is This Right?
Here’s a simple example: You have a stored procedure that accepts start and end date parameters.
What do you want to do if someone passes in a start date that’s after the end date?
- Throw an error and call someone a bonehead
- Be nice and swap them around
- Be psychic and change them to dates that they probably meant
- Add or subtract time from one or both of the parameters
But let me ask you: if this is coming from a a front end application, why would you wait until the query gets to SQL Server to figure out that the start date is after the end date?
The application should tell you that. The application is capable of telling you that. The application server is a lot cheaper than the SQL Server.
You don’t need error handling in SQL Server for that.
Is This Permanent?
There may be times when it’s sensible to halt a stored procedure when it hits an error.
Let’s say a critical step is to populate a temporary object. If that step fails, all further effort is wasted because no results will be returned.
You could also have a check to look at the ROWCOUNT_BIG() after the insert to have it bail if zero rows end up in the temporary object, too. But that’s not an error, really, that’s just pure human kindness.
Other times, like if your query is just returning a bunch of selects (or if a minor snag somewhere only has a minor impact on the overall results), an error in one place might not be enough of an issue to stop everything.
- If you want everything to stop and rollback, use
SET XACT_ABORT ON
; at the beginning of your stored procedure. - If you want to accept small errors as long as you get the larger result set, don’t do anything.
- If you want to log and swallow transient errors, use an empty TRY/CATCH block around queries that may not matter to the outcome.
There are situations where all of those things make sense. Make sure you choose the right one for your arrangement.
A good example of this is in sp_BlitzLock. Sometimes part of the script will fail to get Agent job names depending on permissions and stuff, but that doesn’t impact the overall analysis much.
Try And Retry
There are plenty of good uses for retrying things. Like the start/end date example above, this is usually more wisely done in application code.
But if your stored procedure isn’t being called by an application, doing it in T-SQL isn’t a horrible alternative.
In my life, I’ve used retry logic in stored procedures to:
- Catch deadlock errors and attempt the modification again
- Wait for a lock timeout and try again
- Check in sp_getapplock locks and try again
- Look for available records in a queue and try again
I’m sure there are more, but these are some simple examples where writing retry logic into T-SQL was the best available option.
You Don’t Always Need Error Handling
Some processes require a certain amount of robustness in terms of a few things:
- Error handling and logging
- Retrying certain types of transactions
- Self-healing in the event of certain failures
I didn’t talk much about the self-healing part, because it’s pretty complicated depending on what you’re doing.
But as a small preview, let’s say you’re building a process to move data from one big table to a more normalized schema.
You have to anticipate failures, and be able to pick back up from any point when the process is re-executed.
Exciting times we live in, no?
Thanks for reading!
Going Further
If this is the kind of SQL Server stuff you love learning about, you’ll love my training. I’m offering a 75% discount to my blog readers if you click from here. I’m also available for consulting if you just don’t have time for that, and need to solve database performance problems quickly. You can also get a quick, low cost health check with no phone time required.