I read that many tend to SET XACT_ABORT ON at the beginning of the procedure.
CREATE PROC myProc
AS
Begin
BEGIN TRAN
SET XACT_ABORT ON
[..code1 that might throw an error..]
[..code2..]
SET XACT_ABORT OFF [?]
COMMIT TRAN
END
SET XACT_ABORT OFF [?]
Because run-time errors will terminate the procedure, SET XACT_ABORT will be left as ON. I have some questions :
Where do you set it on ? Before the CREATE PROC definition, after BEGIN or after BEGIN TRAN ? SET is at connection level so I suppose all three would not make a difference ?
When do you turn it OFF if it is left ON when errors occur ?
Setting XACT_ABORT before the CREATE PROC definition doesn't have much sense. Unlike ANSI_NULLS or QUOTED_IDENTIFIER, this option is not stored as a property of the stored procedure. I would say that you should set XACT_ABORT immediately after CREATE PROC myProc AS or after the first BEGIN, where you would also place the SET NOCOUNT ON.
I would not bother setting XACT_ABORT off. If any code portion needs to have it off (for example, inside a TRY-CATCH block that ignores some errors), it should set it off inside that specific TRY block, and set it back on at the end of END CATCH.
For more info, see: http://www.sommarskog.se/error_handling/Part1.html
Related
We have recently been parachuted to a new ETL project with very bad code.
I have in my hands a query with 700 rows and all sort of update.
I would like to debug it with SET XACT_ABORT ON; and the goal is to rollback everything if only one transaction fails.
But I find several way to archive it on StackOverflow like this:
BEGIN TRANSACTION;
BEGIN TRY
-- Multiple sql statements goes here
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
END CATCH
or this:
BEGIN TRY
BEGIN TRANSACTION
-- Multiple sql statements goes here
COMMIT TRANSACTION
END TRY
BEGIN CATCH
PRINT(ERROR_MESSAGE())
ROLLBACK TRANSACTION
END CATCH
and none of these uses SET XACT_ABORT ON;.
I don't understand, is SET XACT_ABORT ON the same as using BEGIN TRY BEGIN TRANSACTION?
Can I just use:
SET XACT_ABORT ON;
-- Multiple sql statements goes here
and get ridof all the:
BEGIN TRANSACTION;
BEGIN TRY
?
And also, should I use BEGIN TRANSACTION and then BEGIN TRY or the other way around?
It is not the same. It decides when errors are thrown.
You should always use SET XACT_ABORT ON, because it is more consistent; almost always, an error will stop execution and throw an error. Else, half things throw errors and the other half continue execution.
There is a great article about this whole subject on Erland Sommarskog's site, and if you go at this point you will see a table which describes this strange behaviour. In conclusion, I recommend the General Pattern for Error Handling, as it is very well documented as well as provide you the opportunity to tweak it according to its own documentation.
Thank you for the resources #George Menoutis.
I post here my practical solution:
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRANSACTION;
-- Multiple sql statements goes here
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0 ROLLBACK TRANSACTION;
THROW;
END CATCH;
GO
SET XACT_ABORT OFF;
If you have XACT_ABORT ON there is no need to manually catch any errors, unless you are doing error logging. XACT_ABORT will cause all errors to doom the transaction and roll it back.
All you need is
SET XACT_ABORT ON;
BEGIN TRAN;
--do stuff
COMMIT;
If there is only one statement then you don't even need BEGIN TRAN; and COMMIT;
I'm not advanced programmer in SQL and maybe my question is silly, but I haven't found an answer in google. We have some SQL construction for implementing packages of changes:
...
BEGIN TRY
BEGIN TRANSACTION;
<User Code Is Here>
...
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
...
END CATCH;
...
How can I put the chain of CREATE TRIGGER blocks instead of <User Code Is Here> without errors:
-- Table1
CREATE TRIGGER trTable1_Dates ON dbo.Table1
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
...
SET NOCOUNT OFF;
END
GO
...
-- TableN
CREATE TRIGGER trTableN_Dates ON dbo.TableN
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
...
SET NOCOUNT OFF;
END
GO
The purpose is to create all triggers or nothing and print message in CATCH block of code if fails.
Edited
The errors are:
On first trigger's BEGIN: SQL80001: Incorrect syntax near 'BEGIN'. Expecting EXTERNAL.
After first trigger, on GO: SQL80001: Incorrect syntax near 'GO'.
END TRY: SQL80001: Incorrect syntax near 'TRY'. Expecting CONVERSATION.
END CATCH: SQL80001: Incorrect syntax near 'CATCH'. Expecting CONVERSATION.
You need to run each create trigger statement in a separate scope/batch (because it has to be the first statement in the batch). So you'll have to escape any quotes in the trigger definitions too:
BEGIN TRY
BEGIN TRANSACTION;
exec sp_executesql N'CREATE TRIGGER trTable1_Dates ON dbo.Table1
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
//An empty string in here has to be '''' to escape the quotes
SET NOCOUNT OFF;
END'
exec sp_executesql N'CREATE TRIGGER trTableN_Dates ON dbo.TableN
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
...
SET NOCOUNT OFF;
END'
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
...
END CATCH;
Transactions are orthogonal to batches and nested scopes, so the transaction covers all activity that occurs inside each EXEC too.
It's a best practice to put SET OPTIONS at the top of a stored proc. It's also a best practice to have BEGIN and END wrap the entire stored proc body. Should the SET OPTIONS be wrapped in BEGIN/END, or does it not matter?
I.e.:
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN
...
END
or
BEGIN
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
...
END
I used to use this code snippet within my stored procedure in SQL Server:
create procedure proc_name
--declare variables
as
set nocount on
begin transaction
begin try
--do something
commit transaction
end try begin catch
rollback transaction
;throw
end catch
go
but today I got to know 'set xact_abort on' statement.
Is the following code equivalent to previous one? Are there any differences between them?
create procedure proc_name
--declare variables
as
set nocount on
set xact_abort on
begin transaction
--do something
commit transaction
go
Quoting from MS docs
A TRY…CATCH construct catches all execution errors that have a severity higher than 10 that do not close the database connection.
So, try catch does not catch all possible errors. You can use xact_abort on in addition to try catch.
try/catch give you more flexibility, i.e., you are not limited to just a rollback when something is not happy.
I want to set SET XACT_ABORT ON in a SQL Server 2008R2 stored procedure with a transaction, so do it in a creation script:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET XACT_ABORT ON
GO
CREATE PROCEDURE MyProc
AS
BEGIN TRAN
...
IF ##ERROR <> 0
BEGIN
GOTO Done
END
...
IF ##ERROR <> 0
BEGIN
GOTO Done
END
COMMIT TRAN
Done:
IF ##ERROR <> 0
BEGIN
ROLLBACK TRAN
END
GO
After successful creation, I check the transaction by clicking "Modify" stored procedure option and in a generated ALTER PROCEDURE script I don't see SET XACT_ABORT ON line:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE MyProc
AS
BEGIN TRAN
...
Where am I wrong or what is the trick? How to correctly define SET XACT_ABORT ON?
You normally set xact_abort as part of the body of the stored procedure:
CREATE PROCEDURE MyProc
AS
SET XACT_ABORT ON
BEGIN TRAN
....
There are two "special" settings that are remembered from the session that created the procedure. Explanation from MSDN:
Stored procedures execute with the SET settings specified at execute
time except for SET ANSI_NULLS and SET QUOTED_IDENTIFIER. Stored
procedures specifying SET ANSI_NULLS or SET QUOTED_IDENTIFIER use the
setting specified at stored procedure creation time. If used inside a
stored procedure, any SET setting is ignored.
So when you create a stored procedure, SQL Server copies the QUOTED_IDENTIFIER option from the connection to the procedure definition. The goal is that someone else with a different QUOTED_IDENTIFIER setting still gets the behavior the author of the procedure intended.
The same is not true for XACT_ABORT.
You didn't mention whether or not you are using SQL Management Studio, but if you are and click "Modify" on an existing stored procedure (which I'm assuming is what you did) then MS just generates a boilerplate script based on the contents of the existing stored procedure.
You might consider defining your stored procedures in a separate script file that performs and ALTER PROCEDURE, plus whatever other options you want outside of the sproc (such as SET XACT_ABORT ON). That way you have more control and can just execute the script to update the sproc.