Semicolon before throw statement - sql-server

Why is a semicolon required before a throw statement?
https://learn.microsoft.com/en-us/sql/t-sql/language-elements/throw-transact-sql

It can resolve some ambiguities
Compare
BEGIN TRAN THROW --Starts a transaction named "THROW"
BEGIN TRY
SELECT 1 / 0
END TRY
BEGIN CATCH
ROLLBACK TRAN
THROW /*Rolls back the transaction named "THROW"*/
END CATCH
To
BEGIN TRAN THROW --Starts a transaction named "THROW"
BEGIN TRY
SELECT 1 / 0
END TRY
BEGIN CATCH
ROLLBACK TRAN; /*rolls back the tran without caring about the name*/
THROW --rethrows the error
END CATCH

Related

Transaction won't rollback

I have the following transaction. I want it to rollback if the SUM of points are not equal to 4000 and to COMMIT if it is. I execute the transaction and the number of points is not equal to 4000. But the transaction is executed nonetheless. Why is that?
BEGIN TRAN [Whist]
BEGIN TRY
END TRY
BEGIN CATCH
IF (SELECT SUM(Point) FROM dbo.Players) <> 4000
ROLLBACK TRANSACTION [Whist]
END CATCH
IF (SELECT SUM(Point) FROM dbo.Players) = 4000
COMMIT TRAN
Please run this SQL 2 ways. Once with #sum_of_points set to 4000 which will commit the transaction. Second time with #sum_of_points set to 4001 which will rollback the transaction.
BEGIN TRAN
BEGIN TRY
declare #sum_of_points int=4000;
IF (#sum_of_points <> 4000)
throw 50000, 'Not equals 4000 so do not commit the transaction', 1;
commit transaction;
print ('transaction was committed')
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
print ('transaction has been rolled back');
END CATCH

SQL Server mismatching number of BEGIN and COMMIT statements nested transaction

I followed a recommended template for error handling in a transaction that should work when it's executed inside an existing transaction.
This is my template
CREATE PROCEDURE DoSomething
AS
BEGIN
SET NOCOUNT ON
DECLARE #trans INTEGER = ##TRANCOUNT
IF (#trans > 0)
SAVE TRANSACTION SavePoint
ELSE
BEGIN TRANSACTION
BEGIN TRY
-- code with a check that does a THROW if the requirements aren't met
IF (#trans = 0)
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF (#trans > 0)
ROLLBACK TRANSACTION SavePoint
ELSE
ROLLBACK TRANSACTION
;THROW
END CATCH
END
If I replace the THROW within the TRY block with a RAISERROR, the issue remains.
Test results:
EXEC fail scenario within transaction: Correct result (gives the right error message)
EXEC success scenario within transaction: Gives unexpected error.
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 2.
EXEC fail scenario outside transaction: Gives expected error.
EXEC success scenario outside transaction: Gives unexpected error. The error is the same as above, but every time you execute it, it increments by -1. Does this mean each time more stuff stays uncommitted?
This is how a test looks like:
BEGIN TRANSACTION
EXEC ...
ROLLBACK TRANSACTION
Does anyone know what's going wrong?
Uncomment var
CREATE PROCEDURE DoSomething
AS
BEGIN
SET NOCOUNT ON
DECLARE #trans INTEGER = ##TRANCOUNT
IF (#trans > 0)
SAVE TRANSACTION SavePoint
ELSE
BEGIN TRANSACTION
BEGIN TRY
DECLARE #f float;
SET #f = 0;
--var 1.
--print 1/0
--var 2.
print LOG(#f)
--var ok
--print 'ok'
END TRY
BEGIN CATCH
IF (#trans > 0 AND XACT_STATE() <> -1)
BEGIN
PRINT 'ROLLBACK SavePoint'
ROLLBACK TRANSACTION SavePoint
END
PRINT 'Error'
END CATCH
END
And execute
BEGIN TRANSACTION
EXEC DoSomething
IF XACT_STATE() = -1
BEGIN
PRINT 'ROLLBACK XACT'
ROLLBACK
END
IF ##TRANCOUNT > 0
BEGIN
PRINT 'COMMIT'
COMMIT
END

Procedure with multiple transactions how to set the error message to the output parameter

In Sql Server 2008 R2, I am creating a procedure with multiple transactions in it. Try..Catch Block is used for each transaction. I use output parameter to catch the error code and message since it will be caught by the main program. But When there is errors the output parameter are not correct set to the error message and code, what is problem here?
create procedure xxx (#P_Return_Status VARCHAR(1) OUTPUT, #P_Error_Code INT OUTPUT,)
AS
BEGIN
BEGIN TRY
BEGIN TRANSACTION TR1
.....
COMMIT TRANSACTIOn TR1
END TRY
BEGIN CATCH
IF (##TRANCOUNT > 0)
BEGIN
ROLLBACK TRANSACTION TR1
END
Set #P_Error_Code = Error_Number();
Set #P_Error_Messages = LEFT(ERROR_MESSAGE (), 2000)
END CATCH
BEGIN TRY
BEGIN TRANSACTION TR2
.....
COMMIT TRANSACTIOn TR2
END TRY
BEGIN CATCH
IF (##TRANCOUNT > 0)
BEGIN
ROLLBACK TRANSACTION TR2
END
Set #P_Error_Code = Error_Number();
Set #P_Error_Messages = LEFT(ERROR_MESSAGE (), 2000)
END CATCH
END
GO
Any help would be really appreciated!
I just don't see the point of putting two transaction inside this one procedure, just put all of the statements in one transaction and commit it or rollback it.
if it does need to be in separate transactions put these both try...catch in two separate procedures and call one sp from another sp's try block.
create procedure xxx
#P_Return_Status INT OUTPUT
,#P_Error_Code INT OUTPUT
,#P_Error_Messages VARCHAR(2000) OUTPUT
AS
BEGIN
BEGIN TRY
BEGIN TRANSACTION TR1
/* put statements from both transaction here
or
statements for TR1
AND
call the procedure containing code for TR2
*/
COMMIT TRANSACTIOn TR1
SET #P_Return_Status = 1;
END TRY
BEGIN CATCH
IF (##TRANCOUNT > 0)
BEGIN
ROLLBACK TRANSACTION TR1
END
Set #P_Error_Code = Error_Number();
Set #P_Error_Messages = LEFT(ERROR_MESSAGE (), 2000)
SET #P_Return_Status = 0;
END CATCH
END
GO

T-SQL error handling with try catch rollback, error rows were deleted

I have the following construct. I noticed that erroneous rows where error number was clearly > 0 was being deleted at the end of the while loop. I don't understand where i have gone wrong with the error catching. Do i need the second commit in the catch section to commit the update of the error number?
while ##FETCH_STATUS = 0
begin try
begin transaction
[...insert something into a table here...]
set #countrec = #countrec + ##rowcount
update Alarmtable
set success = 0
where Recno = #recno
commit transaction
end try
begin catch
if ##trancount > 0 rollback transaction
select #error = error_number()
, #errormsg = error_message()
update Alarmtable
set success = #error
where Recno = #recno
if ##trancount > 0 commit transaction
end catch
fetch next from listofrecords into
#recno, #alarmcontent
end /* while */
close listofrecords
deallocate listofrecords
delete Alarmtable
where success = 0
Removed the commit transaction and the error entries don't get removed anymore. Thanks #kevchadders.

Replacing RAISERROR with THROW

In my procedures historically, I've always caught an exception, then raised it after a ROLLBACK. I see in MSDN that the recommended approach (for SQL2012+) is to THROW.
Based on this example procedure:
CREATE PROC my_procName
#id int
AS
BEGIN TRY
BEGIN TRAN
UPDATE [tbl_A] WHERE (ID=#id);
UPDATE [tbl_B] WHERE (fkID=#id);
UPDATE [tbl_C] WHERE (fkID=#id);
COMMIT TRAN
END TRY
BEGIN CATCH
ROLLBACK TRAN
DECLARE #ErrMsg nvarchar(4000)
DECLARE #ErrSeverity int
SET #ErrMsg = ERROR_MESSAGE()
SET #ErrSeverity = ERROR_SEVERITY()
RAISERROR(#ErrMsg, #ErrSeverity, 1)
END CATCH
GO
Is this the correct way to throw the exception, while preserving the ROLLBACK?
CREATE PROC my_procName
#id int
AS
BEGIN TRY
BEGIN TRAN
UPDATE [tbl_A] WHERE (ID=#id);
UPDATE [tbl_B] WHERE (fkID=#id);
UPDATE [tbl_C] WHERE (fkID=#id);
COMMIT TRAN
END TRY
BEGIN CATCH
ROLLBACK TRAN
THROW
END CATCH
GO
I have already looked at MSDN, Google, and this site for examples, but none include the ROLLBACK, so this is just a quick question to make absolutely sure.
Just for the record statement before THROW statement should be terminated by semicolon. But generally your approach is correct - THROW ends the batch therefore must be the last statement you want to execute in your catch block. Optionally you can use THROW with parameters:
THROW [ { error_number | #local_variable },
{ message | #local_variable },
{ state | #local_variable } ]
[ ; ]

Resources