SQL While Loop to retry Transaction until success - sql

I need to have a transaction, which when it fails, rolls back, and retries.
Where's what I have so far:
CREATE PROCEDURE [dbo].[Save]
#MasterID as bigint
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
Declare #successtrans bit = 0
While #successtrans = 0
Begin -- while
BEGIN TRY
BEGIN TRANSACTION
DELETE SomeRecords WHERE ParentRecordID = #MasterID
INSERT INTO SomeRecords
Select FieldOne, FieldTwo, FieldThree, ParentRecordID
FROM OtherRecords
WHERE ParentRecordID = #MasterID
INSERT INTO AnotherTable
SELECT FieldA, FieldB, FieldC, FieldD
FROM MoreRecords
WHERE FieldD = #MasterID
COMMIT TRANSACTION
set #successtrans = 1
END TRY
BEGIN CATCH
IF(XACT_STATE()) = -1 -- Determine the state of the current transction (1 = committable, 0 = no active transaction (same as ##Trancount check), -1 = active, but uncommittable) only on severity 17 or higher
BEGIN
DECLARE #ErrorNumber bigint = ERROR_Number(),
#ErrorSeverity int = ERROR_SEVERITY(),
#ErrorState int = ERROR_STATE(),
#ErrorProcedure varchar(100) = ERROR_PROCEDURE(),
#ErrorLine int = ERROR_LINE(),
#ErrorMessage varchar(Max) = ERROR_MESSAGE()
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION
INSERT INTO ErrorLog(ErrorNumber, ErrorSeverity, ErrorState, ErrorProcedure, ErrorLine, ErrorMessage, ErrorDate)
VALUES(#ErrorNumber, #ErrorSeverity, #ErrorState, #ErrorProcedure, #ErrorLine, #ErrorMessage, GETDATE())
END -- if
END CATCH
End -- while
END -- proc
GO
The question is, will this work for what I'm doing? Logic says yes. But this is a methodology I need to implement across 10 or more stored procs, and I'm having trouble making it fail and repeat.

You should check in the catch why it's failing or you could generate an infinite loop (Like already existing primary-keys).
As I don't know why you want to do this I won't say it's a bad design, but it's really suspicious. If it's failing is for some reason and exception message will say you why, instead of "brute forcing" your database until it does what you're expecting you should attack the cause of the problem (Bad data? Concurrency? Lot of causes could be causing the exception)
Edit: You should take care of your log too, on an infinite loop it will fill you hdd eventually

Related

Stored procedure - do not execute if any part of the procedure fails

SQL Server 2008
I have a stored procedure that selects data from a source, inserts it into a staging table, deletes some rows from the fact table and then inserts more rows from the staging table into the mentioned fact table.
I would like to not execute the procedure if ANY part of the code fails. So for example if deletion of the rows fail because of some reason I would like to have the stage table also unchanged.
Is it possible? To 'check' the code and insert tables before running the code?
Edit: thank you! I implemented one of the solutions you suggested. The thing is I have a procedure nested in another procedure (try catch nested in another try catch) - I want to catch the errors and insert them to the errorlog table I created. The error catching worked as expected before but after the changes errors are not inserted to the table anymore and I'm getting the error:
The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction.
The query I'm running is the MAIN procedure (below)
the structure of my first query is:
[procedure instertdata]
BEGIN TRANSACTION
BEGIN TRY
INSERT INTO Your_Table (Col1, Col2)
VALUES ....
UPDATE Your_Table
SET [Col1] = .......
WHERE ........
COMMIT TRANSACTION
END TRY
BEGIN CATCH
INSERT INTO MyErrorLogTable
SELECT AffectedTable = 'mytable',
ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_STATE() AS ErrorState,
ERROR_PROCEDURE() AS ErrorProcedure,
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage,
GETDATE() AS TimeAndDate;
IF ##trancount > 0 ROLLBACK TRANSACTION
END CATCH
and the code of the main procedure is:
[procedure main]
BEGIN TRY
EXEC [procedure instertdata]
END TRY
BEGIN CATCH
INSERT INTO MyErrorLogTable
SELECT ProcedureName= 'mytable',
ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_STATE() AS ErrorState,
ERROR_PROCEDURE() AS ErrorProcedure,
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage,
GETDATE() AS TimeAndDate;
END CATCH
What I'm doing wrong? How to handle it?
EDIT:
I mentioned before I have a procedure nested in another one. What I did I moved the transaction rollback to the outer one. The MAIN procedure doesn't have transaction rollback.. procedure is nested So it looks like follows:
CREATE PROCEDURE [dbo].[myload]
AS
BEGIN
set nocount on;
declare #trancount int;
set #trancount = ##trancount;
begin try
if #trancount = 0
begin transaction
else
save transaction [myload];
EXEC MAIN_procedure
lbexit:
if #trancount = 0
commit;
END TRY
BEGIN CATCH
declare #error int, #severity varchar(4000), #estate varchar(55), #procedure varchar (55), #eline varchar (255), #emessage varchar (4000), #edate date;
SELECT #error=ERROR_NUMBER(),
#severity=ERROR_SEVERITY(),
#estate=ERROR_STATE() ,
#procedure=ERROR_PROCEDURE(),
#eline=ERROR_LINE(),
#emessage= ERROR_MESSAGE() ,
#edate=GETDATE();
if XACT_STATE() = -1
rollback;
if XACT_STATE() = 1 and #trancount = 0
rollback
if XACT_STATE() = 1 and #trancount > 0
rollback transaction [mytransaction];
INSERT INTO myErrorTable
SELECT ProcedureName= 'myload',
#error,#severity, #estate, #procedure, #eline, #emessage, #edate;
END CATCH
END
Seems to work so far so thank you all for your help!
Transaction block will ROLLBACK all the transactions, if any one of the transaction fails inside the transaction block.
Don't use TRUNCATE inside the transaction block, because truncate statements never rolled back.
BEGIN TRANSACTION [Tran1]
BEGIN TRY
INSERT INTO Your_Table (Col1, Col2)
VALUES ....
UPDATE Your_Table
SET [Col1] = .......
WHERE ........
COMMIT TRANSACTION [Tran1]
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION [Tran1]
END CATCH
You have to use Savepoint feature to save part of the transaction, i.e., saving results to staging table. I have utilized Remus Rusunu nested transaction approach with save points. You can read the same here
declare #trancount int;
set #trancount = ##trancount;
BeginTry
if #trancount = 0
BEGIN TRANSACTION
INSERT INTO StagingTable...
SAVE TRANSACTION StagingTableInsertion
DELETE FROM FactTable WHERE <CONDITION>...
INSERT INTO FactTable...
SELECT * FROM StagingTable
IF #tranCount = 0
COMMIT TRANSACTION
EndTry
BeginCatch
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 StagingTableInsertion;
raiserror ('usp_my_procedure_name: %d: %s', 16, 1, #error, #message) ;
EndCatch
Read more on SavePoints on mssqltips
You can use a try/catch statement and raiserror to return the control back to main procedure in the catch rollback the transaction.
But I may suggest instead of playing with the fact table, create a second staging table like 'staging_table_2' and manipulate the data in this staging, you can refer to fact tables in joins and then use a simple merge to populate the fact table.
This way you will always be sure data in your fact is clean and failures is not impacting any data inconsistency.

sql try/catch rollback/commit - preventing erroneous commit after rollback

I am trying to write an MS sql script that has a transaction and a try/catch block. If it catches an exception, the transaction is rolled back. If not, the transaction is committed. I have seen a few different websites saying to do it like this:
begin transaction
begin try
--main content of script here
end try
begin catch
rollback transaction
end catch
commit transaction
But won't we still hit the "commit transaction" line even in the case of catching an exception? Won't this lead to a SQL error because the transaction has already been rolled back? I think it should be done like this:
declare #success bit = 1
begin transaction
begin try
--main content of script here
end try
begin catch
rollback transaction
set #success = 0
end catch
if(#success = 1)
begin
commit transaction
end
Howcome the commonly-posted solution does not include the #success variable? Is there no sql error that happens as a result of committing a transaction that has already been rolled back? Am I incorrect in saying that the "commit transaction" line of the first code example will still be hit in the case of catching an exception?
I always thought this was one of the better articles on the subject. It includes the following example that I think makes it clear and includes the frequently overlooked ##trancount which is needed for reliable nested transactions
PRINT 'BEFORE TRY'
BEGIN TRY
BEGIN TRAN
PRINT 'First Statement in the TRY block'
INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(1, 'Account1', 10000)
UPDATE dbo.Account SET Balance = Balance + CAST('TEN THOUSAND' AS MONEY) WHERE AccountId = 1
INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(2, 'Account2', 20000)
PRINT 'Last Statement in the TRY block'
COMMIT TRAN
END TRY
BEGIN CATCH
PRINT 'In CATCH Block'
IF(##TRANCOUNT > 0)
ROLLBACK TRAN;
THROW; -- raise error to the client
END CATCH
PRINT 'After END CATCH'
SELECT * FROM dbo.Account WITH(NOLOCK)
GO
In your first example, you are correct. The batch will hit the commit transaction, regardless of whether the try block fires.
In your second example, I agree with other commenters. Using the success flag is unnecessary.
I consider the following approach to be, essentially, a light weight best practice approach.
If you want to see how it handles an exception, change the value on the second insert from 255 to 256.
CREATE TABLE #TEMP ( ID TINYINT NOT NULL );
INSERT INTO #TEMP( ID ) VALUES ( 1 )
BEGIN TRY
BEGIN TRANSACTION
INSERT INTO #TEMP( ID ) VALUES ( 2 )
INSERT INTO #TEMP( ID ) VALUES ( 255 )
COMMIT TRANSACTION
END TRY
BEGIN CATCH
DECLARE
#ErrorMessage NVARCHAR(4000),
#ErrorSeverity INT,
#ErrorState INT;
SELECT
#ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE();
RAISERROR (
#ErrorMessage,
#ErrorSeverity,
#ErrorState
);
ROLLBACK TRANSACTION
END CATCH
SET NOCOUNT ON
SELECT ID
FROM #TEMP
DROP TABLE #TEMP
I used below ms sql script pattern several times successfully which uses Try-Catch,Commit Transaction- Rollback Transaction,Error Tracking.
Your TRY block will be as follows
BEGIN TRY
BEGIN TRANSACTION T
----
//your script block
----
COMMIT TRANSACTION T
END TRY
Your CATCH block will be as follows
BEGIN CATCH
DECLARE #ErrMsg NVarChar(4000),
#ErrNum Int,
#ErrSeverity Int,
#ErrState Int,
#ErrLine Int,
#ErrProc NVarChar(200)
SELECT #ErrNum = Error_Number(),
#ErrSeverity = Error_Severity(),
#ErrState = Error_State(),
#ErrLine = Error_Line(),
#ErrProc = IsNull(Error_Procedure(), '-')
SET #ErrMsg = N'ErrLine: ' + rtrim(#ErrLine) + ', proc: ' + RTRIM(#ErrProc) + ',
Message: '+ Error_Message()
Your ROLLBACK script will be part of CATCH block as follows
IF (##TRANCOUNT) > 0
BEGIN
PRINT 'ROLLBACK: ' + SUBSTRING(#ErrMsg,1,4000)
ROLLBACK TRANSACTION T
END
ELSE
BEGIN
PRINT SUBSTRING(#ErrMsg,1,4000);
END
END CATCH
Above different script blocks you need to use as one block. If any error happens in the TRY block it will go the the CATCH block. There it is setting various details about the error number,error severity,error line ..etc. At last all these details will get append to #ErrMsg parameter. Then it will check for the count of transaction (##TRANCOUNT >0) , ie if anything is there in the transaction for rollback. If it is there then show the error message and ROLLBACK TRANSACTION. Otherwise simply print the error message.
We have kept our COMMIT TRANSACTION T script towards the last line of TRY block in order to make sure that it should commit the transaction(final change in the database) only after all the code in the TRY block has run successfully.
Transaction counter
--##TRANCOUNT = 0
begin try
--##TRANCOUNT = 0
BEGIN TRANSACTION tran1
--##TRANCOUNT = 1
--your code
-- if failed ##TRANCOUNT = 1
-- if success ##TRANCOUNT = 0
COMMIT TRANSACTION tran1
end try
begin catch
print 'FAILED'
end catch
Below might be useful.
Source: https://msdn.microsoft.com/en-us/library/ms175976.aspx
BEGIN TRANSACTION;
BEGIN TRY
-- your code --
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber
,ERROR_SEVERITY() AS ErrorSeverity
,ERROR_STATE() AS ErrorState
,ERROR_PROCEDURE() AS ErrorProcedure
,ERROR_LINE() AS ErrorLine
,ERROR_MESSAGE() AS ErrorMessage;
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION;
END CATCH;
IF ##TRANCOUNT > 0
COMMIT TRANSACTION;
GO

Batchwise Script Execution

I have long script which contains the Create tables, create schemas,insert data,update tables etc.I have to do this by only on script in batch wise.I ran it before but it created every time some error due to this some object will present inside the database. So In need some mechanism which can handle the batch execution if something goes wrong the whole script should be rolled back.
Appreciated Help and Time.
--343
Try this:
DECLARE #outer_tran int;
SELECT #outer_tran = ##TRANCOUNT;
-- find out whether we are inside the outer transaction
-- if yes - creating save point if no starting own transaction
IF #outer_tran > 0 SAVE TRAN save_point ELSE BEGIN TRAN;
BEGIN TRY
-- YOUR CODE HERE
-- if no errors and we have started own transaction - commit it
IF #outer_tran = 0 COMMIT;
END TRY
BEGIN CATCH
-- if error occurred - rollback whole transaction if it is own
-- or rollback to save point if we are inside the external transaction
IF #outer_tran > 0 ROLLBACK TRAN save_point ELSE ROLLBACK;
--and rethrow original exception to see what happens
DECLARE
#ErrorMessage nvarchar(max),
#ErrorSeverity int,
#ErrorState int;
SELECT
#ErrorMessage = ERROR_MESSAGE() + ' Line ' + cast(ERROR_LINE() as nvarchar(5)),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE();
RAISERROR (#ErrorMessage, #ErrorSeverity, #ErrorState);
END CATCH
While I might not have caught all the nuances of your question, I believe XACT_ABORT will deliver the functionality you seek. Simply add a
SET XACT_ABORT ON;
to the beginning of your script.
With the 2005 release of SQL Server, you have access to try/catch blocks in TSQL as well.

SQL Server error handling pattern

I am not an expert on SQl Server. Is this a valid pattern for handling errors in a batch of SELECT, INSERT...in SQl SERVER ? (I use v.2008)
BEGIN TRANSACTION
BEGIN TRY
-- statement 1
-- statement 2
-- statement 3
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
END CATCH
Thanks
I use something like this:
CREATE PROCEDURE ErrorHandlingPattern
( #intParam int
,#varcharParam varchar(10)
,#dateParam datetime
)
AS
BEGIN TRY
SET NOCOUNT ON
DECLARE #Rows int --store ##ROWCOUNT in this
,#ErrorMsg varchar(500) --temp string to build the contents of messages passed into RAISERROR calls
,#LogInfo varchar(5000) --will hold any info necessary for error debugging, append to this throughout the procedure with important info
,#TransactionCount int
SELECT #TransactionCount=##TRANCOUNT
,#LogInfo='#intParam=' +ISNULL(''''+CONVERT(varchar(10), #intParam )+'''','NULL')
+', #varcharParam=' +ISNULL(''''+ #varcharParam +'''','NULL')
+', #dateParam=' +ISNULL(''''+CONVERT(varchar(10), #dateParam,121 )+'''','NULL')
+'; ##TRANCOUNT=' +ISNULL(''''+CONVERT(varchar(10), ##TRANCOUNT )+'''','NULL')
--validate parameters
IF #intParam ....
BEGIN --logical error
SET #ErrorMsg='Error, invalid value for #intParam: '+ISNULL(''''+CONVERT(varchar(10),#intParam)+'''','NULL')
RAISERROR(#ErrorMsg,16,1) --send control to the BEGIN CATCH block
END
IF #TransactionCount=0 --if we are already in a transaction, no need to start another, nesting transactions +rollback=warnings about transaction count not being the same as when the procedure started.
BEGIN
BEGIN TRANSACTION
END
--do your work here....
INSERT/UPDATE/DELETE...
SELECT #Rows=##ROWCOUNT
IF #Rows!=ExpectedValue
BEGIN --logical error
SET #ErrorMsg='Error, INSERT/UPDATE/DELETE of tableXYZ resulted in '+ISNULL(''''+CONVERT(varchar(10),#Rows)+'''','NULL')+' rows affected'
RAISERROR(#ErrorMsg,16,1) --send control to the BEGIN CATCH block
END
--append improtant info to log string
SET #LogInfo=ISNULL(#LogInfo,'')+'; INSERT/UPDATE/DELETE of tableXYZ resulted in '+ISNULL(''''+CONVERT(varchar(10),#Rows)+'''','NULL')+' rows affected'
IF #TransactionCount=0 --only end the transaction if it started here
BEGIN
COMMIT --put in try block to be able to catch any problems committing
END
END TRY
BEGIN CATCH
IF XACT_STATE()!=0 --if there is any error end the transaction ASAP
BEGIN
ROLLBACK TRANSACTION
END
--will echo back the complete original error message
DECLARE #ErrorMessage nvarchar(400), #ErrorNumber int, #ErrorSeverity int, #ErrorState int, #ErrorLine int
SELECT #ErrorMessage = N'Error %d, Line %d, Message: '+ERROR_MESSAGE(),#ErrorNumber = ERROR_NUMBER(),#ErrorSeverity = ERROR_SEVERITY(),#ErrorState = ERROR_STATE(),#ErrorLine = ERROR_LINE()
RAISERROR (#ErrorMessage, #ErrorSeverity, #ErrorState, #ErrorNumber,#ErrorLine)
--because the transaction was ROLLBACKed this insert will be recorded in the database
INSERT INTO YourErrorLog (...) VALUES (...ISNULL(#ErrorMessage,'')+ISNULL(#LogInfo,''))
RETURN 999
END CATCH
RETURN 0
GO
Since you are just doing a batch of a batch of SELECT, INSERT, you can just remove the CREATE PROCEDURE and parameter declarations and have the first line start at BEGIN TRY. Also, because you are not creating a procedure, replace any RETURN statements with GOTO TheEnd and add a TheEnd: label at the script's bottom.
Nearly:
BEGIN TRANSACTION;
BEGIN TRY
-- Generate a constraint violation error.
DELETE FROM Production.Product
WHERE ProductID = 980;
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber
,ERROR_SEVERITY() AS ErrorSeverity
,ERROR_STATE() AS ErrorState
,ERROR_PROCEDURE() AS ErrorProcedure
,ERROR_LINE() AS ErrorLine
,ERROR_MESSAGE() AS ErrorMessage;
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION;
END CATCH;
IF ##TRANCOUNT > 0
COMMIT TRANSACTION;
This was taken from the MSDN documentation on TRY/CATCH where other examples can be found: http://msdn.microsoft.com/en-us/library/ms175976.aspx

Why does Sql Server keep executing after raiserror when xact_abort is on?

I just got surprised by something in TSQL. I thought that if xact_abort was on, calling something like
raiserror('Something bad happened', 16, 1);
would stop execution of the stored procedure (or any batch).
But my ADO.NET error message just proved the opposite. I got both the raiserror error message in the exception message, plus the next thing that broke after that.
This is my workaround (which is my habit anyway), but it doesn't seem like it should be necessary:
if #somethingBadHappened
begin;
raiserror('Something bad happened', 16, 1);
return;
end;
The docs say this:
When SET XACT_ABORT is ON, if a Transact-SQL statement raises a run-time error, the entire transaction is terminated and rolled back.
Does that mean I must be using an explicit transaction?
This is By DesignTM, as you can see on Connect by the SQL Server team's response to a similar question:
Thank you for your feedback. By design, the XACT_ABORT set option does not impact the behavior of the RAISERROR statement. We will consider your feedback to modify this behavior for a future release of SQL Server.
Yes, this is a bit of an issue for some who hoped RAISERROR with a high severity (like 16) would be the same as an SQL execution error - it's not.
Your workaround is just about what you need to do, and using an explicit transaction doesn't have any effect on the behavior you want to change.
If you use a try/catch block a raiserror error number with severity 11-19 will cause execution to jump to the catch block.
Any severity above 16 is a system error. To demonstrate the following code sets up a try/catch block and executes a stored procedure that we assume will fail:
assume we have a table [dbo].[Errors] to hold errors
assume we have a stored procedure [dbo].[AssumeThisFails] which will fail when we execute it
-- first lets build a temporary table to hold errors
if (object_id('tempdb..#RAISERRORS') is null)
create table #RAISERRORS (ErrorNumber int, ErrorMessage varchar(400), ErrorSeverity int, ErrorState int, ErrorLine int, ErrorProcedure varchar(128));
-- this will determine if the transaction level of the query to programatically determine if we need to begin a new transaction or create a save point to rollback to
declare #tc as int;
set #tc = ##trancount;
if (#tc = 0)
begin transaction;
else
save transaction myTransaction;
-- the code in the try block will be executed
begin try
declare #return_value = '0';
set #return_value = '0';
declare
#ErrorNumber as int,
#ErrorMessage as varchar(400),
#ErrorSeverity as int,
#ErrorState as int,
#ErrorLine as int,
#ErrorProcedure as varchar(128);
-- assume that this procedure fails...
exec #return_value = [dbo].[AssumeThisFails]
if (#return_value <> 0)
raiserror('This is my error message', 17, 1);
-- the error severity of 17 will be considered a system error execution of this query will skip the following statements and resume at the begin catch block
if (#tc = 0)
commit transaction;
return(0);
end try
-- the code in the catch block will be executed on raiserror("message", 17, 1)
begin catch
select
#ErrorNumber = ERROR_NUMBER(),
#ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE(),
#ErrorLine = ERROR_LINE(),
#ErrorProcedure = ERROR_PROCEDURE();
insert #RAISERRORS (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
values (#ErrorNumber, #ErrorMessage, #ErrorSeverity, #ErrorState, #ErrorLine, #ErrorProcedure);
-- if i started the transaction
if (#tc = 0)
begin
if (XACT_STATE() <> 0)
begin
select * from #RAISERRORS;
rollback transaction;
insert into [dbo].[Errors] (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
select * from #RAISERRORS;
insert [dbo].[Errors] (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
values (#ErrorNumber, #ErrorMessage, #ErrorSeverity, #ErrorState, #ErrorLine, #ErrorProcedure);
return(1);
end
end
-- if i didn't start the transaction
if (XACT_STATE() = 1)
begin
rollback transaction myTransaction;
if (object_id('tempdb..#RAISERRORS') is not null)
insert #RAISERRORS (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
values (#ErrorNumber, #ErrorMessage, #ErrorSeverity, #ErrorState, #ErrorLine, #ErrorProcedure);
else
raiserror(#ErrorMessage, #ErrorSeverity, #ErrorState);
return(2);
end
else if (XACT_STATE() = -1)
begin
rollback transaction;
if (object_id('tempdb..#RAISERRORS') is not null)
insert #RAISERRORS (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
values (#ErrorNumber, #ErrorMessage, #ErrorSeverity, #ErrorState, #ErrorLine, #ErrorProcedure);
else
raiserror(#ErrorMessage, #ErrorSeverity, #ErrorState);
return(3);
end
end catch
end
Use RETURN immediately after RAISERROR() and it'll not execute the procedure further.
As pointed out on the docs for SET XACT_ABORT, the THROW statement should be used instead of RAISERROR.
The two behave slightly differently. But when XACT_ABORT is set to ON, then you should always use the THROW command.
microsoft suggests using throw instead of raiserror. Use XACT_State to determine commit or rollback for the try catch block
set XACT_ABORT ON;
BEGIN TRY
BEGIN TRAN;
insert into customers values('Mark','Davis','markdavis#mail.com', '55909090');
insert into customer values('Zack','Roberts','zackroberts#mail.com','555919191');
COMMIT TRAN;
END TRY
BEGIN CATCH
IF XACT_STATE()=-1
ROLLBACK TRAN;
IF XACT_STATE()=1
COMMIT TRAN;
SELECT ERROR_MESSAGE() AS error_message
END CATCH