Sql Server - proper syntax for rolling back a transaction - sql-server

The below sql gives a syntax error at RAISEERROR, which goes away if I remove [Tran1] from the query. What is the correct syntax to rollback an aliased transaction and then call RAISERROR?
BEGIN TRY
BEGIN TRANSACTION [Tran1]
...sql goes here...
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION [Tran1]
RAISEERROR ('Error occured')
END CATCH;
Also, if I remove the [Tran1]I get:
Cannot roll back RAISEERROR. No transaction or savepoint of that name
was found.

You do not mention the essential COMMIT TRANSACTION [Tran1] within the 'TRY' block. I presume you have that.
The RAISERROR will raise an error by itself, since it has to be coded like
RAISERROR('Error Occurred',0,0).
A RAISERROR statement with an error will call the BEGIN CATCH section, but there is no transaction anymore to ROLLBACK.

It's better if you don't lose information about the original exception. In SQL Server 2012 and above there is a THROW clause exactly for this purpose. So you can simply do:
BEGIN TRY
BEGIN TRANSACTION [Tran1];
--- Some SQL here --
COMMIT TRANSACTION [Tra1];
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION [Tran1];
THROW;
END CATCH;
In SQL Server 2008R2 and below, it's a bit more cumbersome to preserve the error information:
BEGIN TRY
BEGIN TRANSACTION [Tran1];
--- Some SQL here --
COMMIT TRANSACTION [Tra1];
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION [Tran1];
-- Now throw the exception
DECLARE #ErrorMessage NVARCHAR(4000);
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
SELECT #ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE();
RAISERROR (#ErrorMessage, #ErrorSeverity, #ErrorState);
END CATCH;

Related

SQL transaction commit and error handle

My code is as following:
create my sp
as
declare cursor
open cursor
fetch next from
begin try
begin transaction tran1
exec sp_1....
commit transaction tran1
select * from table
begin transaction tran2
exec sp_2....
commit transaction tran2
end
fetch next from cursor
end try
begin catch
SELECT ERROR_MESSAGE() AS ErrorMessage;
IF ##TRANCOUNT > 0
ROLLBACK TRAN;
fetch next from cursor
end catch
close cursor
deallocate cursor
I need this code to :
contain two transactions and rollback when error happened
when the stored procedure gets killed, the transaction will still rollback
I think that the catch part may not right, because there are two transaction and needs to be specific.

NServiceBus handler and stored procedure call. Should I use SET XACT_ABORT ON or TRY CATCH?

I want to call my ms sql stored procedure from nservicebus handler and I wonder if I still need to use something like
CREATE PROCEDURE [dbo].[proc_fd_SomeEntitySyncRawWithStage]
AS
BEGIN
BEGIN TRY
BEGIN TRANSACTION
-- Batch of inserts and updates that I want to keep transactional
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
DECLARE #ErrorMessage NVARCHAR(4000);
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
SELECT
#ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE();
RAISERROR (#ErrorMessage,
#ErrorSeverity,
#ErrorState
);
END CATCH;
END
Do I really need all this TRY BEGIN TRAN... CATCH ROLLBACK .. or SET XACT_ABORT ON... stuff to rollback my transaction or NServiceBus will rollback it in case of any sql exception in message handler ?
P.S.
Keeping in mind that:
"If a run-time statement error (such as a constraint violation) occurs in a batch, the default behavior in the Database Engine is to roll back only the statement that generated the error"
and
"When SET XACT_ABORT is OFF, in some cases only the Transact-SQL statement that raised the error is rolled back and the transaction continues processing."
Normally, you should always wrap your procedure calls in a transaction, would you use ORM or ADO.NET. Wht ADO.NET you would do something like
var ts = myConnection.BeginTransaction();
and then call your procedure within a try-catch block.
Further depends on your logic. If you procedure could fail because of some logic, you will need to have a return code and throw an exception based on that. If your procedure just fails with SQL exception, the try-catch block will handle it.
In the catch block you need to rollback the transaction.
If you re-throw your exception in the catch block, the message will be retried by FLR and if configured - by SLR. Usually, this helps when having deadlicks in the database, FLR normally handles this.

How to rollback a transaction in a stored procedure?

Looking at the SQL Server Books Online, Microsoft seems to have an (incorrect) method of handling nested transactions in a stored procedure:
Nesting Transactions
Explicit transactions can be nested. This is primarily intended to support transactions in stored procedures that can be called either from a process already in a transaction or from processes that have no active transaction.
The example goes on to show a stored procedure that starts its own transaction ("The procedure enforces its transaction regardless of the transaction mode of any process that executes it."):
CREATE PROCEDURE TransProc #PriKey INT, #CharCol CHAR(3) AS
BEGIN TRANSACTION InProc
...
COMMIT TRANSACTION InProc;
This procedure can then either be called without a transaction running:
EXECUTE TransProc 3,'bbb';
Or with an explicit transaction:
BEGIN TRANSACTION OutOfProc;
EXEC TransProc 1, 'aaa';
COMMIT TRANSACTION OutOfProc
What they don't address is what happens when the stored produre:
fails with an error, but leaves the transaction running
fails with an error, but doesn't leave the transaction running
encounters an error, but continues executing with the transaction open
encounters an error, but continues executing with the transaction rolled back
There is no:
SET XACT_ABORT ON
##TRANCOUNT
anywhere in the canonical example.
If i didn't know any better, i would have thought that the line:
The following example shows the intended use of nested transactions.
should actually read
The following example shows the how not to use nested transactions.
Unless someone can make heads or tails of this BOL example?
You need to use the try catch block with the transaction. So in case you get the error in your catch block then you can rollback your transaction.
Please see the below sql server code for that.
BEGIN TRANSACTION;
BEGIN TRY
-- Some code
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
END CATCH;
CREATE PROCEDURE [usp_my_procedure_name]
AS
BEGIN
SET NOCOUNT ON;
DECLARE #trancount int;
SET #trancount = ##trancount;
BEGIN TRY
IF #trancount = 0
BEGIN TRANSACTION
ELSE
SAVE TRANSACTION usp_my_procedure_name;
-- Do the actual work here
lbexit:
IF #trancount = 0
COMMIT;
END TRY
BEGIN CATCH
DECLARE #error int,
#message varchar(4000),
#xstate int;
SELECT
#error = ERROR_NUMBER(),
#message = ERROR_MESSAGE(),
#xstate = XACT_STATE();
IF #xstate = -1
ROLLBACK;
IF #xstate = 1 AND #trancount = 0
ROLLBACK
IF #xstate = 1 AND #trancount > 0
ROLLBACK TRANSACTION usp_my_procedure_name;
RAISERROR ('usp_my_procedure_name: %d: %s', 16, 1, #error, #message);
END CATCH
END

How do I raise an error that SQL will see as a job failure?

I've got a job scheduled through the SQL Server Agent that runs a sproc which runs some other sprocs. Every sproc looks like this:
BEGIN TRY
-- do stuff
END TRY
BEGIN CATCH
DECLARE #errorMessage varchar(4000)
DECLARE #procName varchar(255)
SELECT #errorMessage = error_message()
SELECT #procName = OBJECT_NAME(##PROCID)
RAISERROR('%s threw an exception: %s', 16, 1, #procName, #errorMessage)
END CATCH
This all works fine - errors are raised and thrown up the stack, life is good. However, my RAISERROR calls don't appear to cause the job to fail - I'm set to receive an e-mail notification "When the job fails," but never receive one. E-mail notifications are working, as I will get emails if I change the notification to "when the job succeeds". Is there some other function I should be using here in place of RAISERROR?
Raise an error in the try block with severity between 11-19 in TRY block and then re-raise the same error in catch block. This will make the step fail..
code snippet from msdn
BEGIN TRY
-- RAISERROR with severity 11-19 will cause execution to
-- jump to the CATCH block.
RAISERROR ('Error raised in TRY block.', -- Message text.
16, -- Severity.
1 -- State.
);
END TRY
BEGIN CATCH
DECLARE #ErrorMessage NVARCHAR(4000);
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
SELECT
#ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE();
-- Use RAISERROR inside the CATCH block to return error
-- information about the original error that caused
-- execution to jump to the CATCH block.
RAISERROR (#ErrorMessage, -- Message text.
#ErrorSeverity, -- Severity.
#ErrorState -- State.
);
END CATCH;
Each job step has an action for step failure, they must set to fail the entire job. Yours is probably set to go to next step only.
If your error severity level is 20 or higher, the database connection will be terminated. This will cause the step to fail. So long as your step is set so that the job fails if the step fails, you'll get your desired outcome.
RAISERROR('%s threw an exception: %s', 20, 1, #procName, #errorMessage) WITH LOG;
SEE msdn description of RAISERROR: https://learn.microsoft.com/en-us/sql/t-sql/language-elements/raiserror-transact-sql

Basic template for Transactions in sqlserver

If I simply wrap my query with:
BEGIN TRANSACTION
COMMIT TRANSACTION
If anything fails inside of that, will it automatically rollback?
From looking at other code, they seem to check for an error, if there is an error then they do a GOTO statement which then calls ROLLBACK TRANSACTION
But that seems like allot of work, to have to check for IF( ##ERROR <> 0) after every insert/update.
I typically do something like this inside my stored procedures. It keeps things nice and safe and passes along any errors that I encounter.
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRANSACTION;
-- Code goes here
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION;
DECLARE
#ERROR_SEVERITY INT,
#ERROR_STATE INT,
#ERROR_NUMBER INT,
#ERROR_LINE INT,
#ERROR_MESSAGE NVARCHAR(4000);
SELECT
#ERROR_SEVERITY = ERROR_SEVERITY(),
#ERROR_STATE = ERROR_STATE(),
#ERROR_NUMBER = ERROR_NUMBER(),
#ERROR_LINE = ERROR_LINE(),
#ERROR_MESSAGE = ERROR_MESSAGE();
RAISERROR('Msg %d, Line %d, :%s',
#ERROR_SEVERITY,
#ERROR_STATE,
#ERROR_NUMBER,
#ERROR_LINE,
#ERROR_MESSAGE);
END CATCH
yes it is important to explicitly rollback the transaction in the case that it does not work.
I usually tell my son you only have to brush the teeth you want to keep.
In this case, you only need to rollback the commands you don't want to execute.
This will automatically rollback the transaction in case off error
SET XACT_ABORT ON
BEGIN TRANSACTION
-- CODE HERE
COMMIT TRANSACTION
For transaction control you use begin, commit and rollback. You begin a transaction by supplying BEGIN TRANSACTION. Then you put the various SQL statements you need. Then you end the transaction by issuing either a commit or rollback. COMMIT TRANSACTION will commit all the changes that you did to the database after the BEGIN statement and make them permanent, so to speak. ROLLBACK TRANSACTION will rollback all changes that you did to the database after the BEGIN statement. However, it will not change variable values.
Example:
BEGIN TRANSACTION
UPDATE table SET column = 'ABC' WHERE column = '123'
COMMIT TRANSACTION
--//column now has a value of 'ABC'
BEGIN TRANSACTION
UPDATE table SET column = 'ABC' WHERE column = '123'
ROLLBACK TRANSACTION
--//column still has it's previous value ('123') No changes were made.

Resources