There are uncommitted transactions - sql

SET XACT_ABORT ON
BEGIN TRY
BEGIN TRAN
INSERT INTO dbo.Student
(FirstName, LastName)
VALUES
('Jon','Ye')
IF XACT_STATE() = 0
COMMIT TRAN
END TRY
BEGIN CATCH
IF XACT_STATE() <> 1
ROLLBACK TRAN
ELSE
COMMIT TRANSACTION
END CATCH
RETURN
GO
Error Message:
There are uncommitted transactionS.
I only see results when I close SQL Server.

the problem is here
IF XACT_STATE() = 0
COMMIT TRAN
XACT_STATE() only returns 0 if no active user transaction exists. Due to the open transaction started by BEGIN TRAN, XACT_STATE must be returning 1 and the COMMIT TRAN consequently does not execute. You should be checking for a return value of 1

Related

How to stop insert in a transaction in SQL Server?

Create query using BEGIN TRAN statement to insert two rows within BEGIN TRY if transaction count is greater than 1, catch error and rollback the transaction, otherwise print “transaction committed” (this is my teacher question the idea is that it should not insert more than 1 row by using the ##trancount is greater than one the transaction is rolled back)
This is the code that I wrote
BEGIN TRY
BEGIN TRAN;
INSERT INTO [Info].[Country]([name]) VALUES ('Italy');
PRINT ##TRANCOUNT
BEGIN TRAN
INSERT INTO [Info].[Country]([name]) VALUES ('Jorden');
PRINT ##TRANCOUNT
IF ##TRANCOUNT > 1
PRINT 'Rollback the transaction...';
ELSE
PRINT 'transaction succeeded';
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
END CATCH;
It keeps inserting the two rows even though the trans count is greater than 1
How can I fix this?!!
This is the result
I suppose you're looking for using ##ROWCOUNT instead of ##TRANCOUNT
BEGIN TRY
BEGIN TRAN MyTran;
INSERT INTO Countries(Name) VALUES
('Italy'),
('Jorden');
IF ##ROWCOUNT > 1
ROLLBACK TRAN MyTran;
ELSE
COMMIT TRAN MyTran;
END TRY
BEGIN CATCH
RAISERROR ('Error Message',
16, -- Severity.
1 -- State.
);
ROLLBACK TRAN MyTran;
END CATCH;
If you really want to use nested transactions and use ##TRANCOUNT
BEGIN TRY
BEGIN TRAN MyTran;
INSERT INTO Countries(Name) VALUES ('Italy');
BEGIN TRAN MySecTran;
INSERT INTO Countries(Name) VALUES ('Jorden');
IF ##TRANCOUNT > 1
ROLLBACK TRAN;
ELSE
COMMIT TRAN;
END TRY
BEGIN CATCH
RAISERROR ('Error Message',
16, -- Severity.
1 -- State.
);
ROLLBACK TRAN;
END CATCH;
Here is a db<>fiddle where you can un-comment one of the two and see how it's working.
Update:
i checked it again. it did not insert any row in the table. but it should insert the first one
Then you need to save the tran as
BEGIN TRY
BEGIN TRAN MyTran;
INSERT INTO Countries(Name) VALUES ('Italy');
SAVE TRAN MySaveTran;
BEGIN TRAN MySecTran;
INSERT INTO Countries(Name) VALUES ('Jorden');
IF ##TRANCOUNT > 1
ROLLBACK TRAN MySaveTran;
COMMIT TRAN;
END TRY
BEGIN CATCH
RAISERROR ('Error Message',
16, -- Severity.
1 -- State.
);
ROLLBACK TRAN;
END CATCH;
BEGIN TRY
BEGIN TRAN;
INSERT INTO [Info].[Country]([name]) VALUES ('Italy');
print ##TRANCOUNT
Begin tran
INSERT INTO [Info].[Country]([name]) VALUES ('Jorden');
print ##TRANCOUNT
if ##TRANCOUNT>1
PRINT 'Rollback the transaction...';
else
print 'transaction successes';
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
END CATCH;
GO
You are not actually doing a rollback, you're just printing 'Rollback the transaction' change to
BEGIN TRY
BEGIN TRAN;
INSERT INTO [Info].[Country]([name]) VALUES ('Italy');
print ##TRANCOUNT
Begin tran
INSERT INTO [Info].[Country]([name]) VALUES ('Jorden');
print ##TRANCOUNT
if ##TRANCOUNT>1
begin
PRINT 'Rollback the transaction...';
ROLLBACK TRANSACTION
end
else
print 'transaction successes';
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
END CATCH;
GO
besides the other mentioned mistakes. I think your teacher wants you to make proper use of the try catch pattern. For this you need to know that the CATCH block is only executed when an error occurs inside of the try block.
See TRY...CATCH (Transact-SQL)
You can raise an error using the THROW statement
THROW (Transact-SQL)
IF ##ROWCOUNT > 1
THROW 51000, 'row count greater 1.', 1;

Why are these nested SQL Server transactions throwing a mismatch error if there is a rollback?

By running 'Test Errors' I get unexpected results. I thought by checking for ##Trancount it would avoid mismatches. Can anyone help me with a better way to rollback errors? I want to rollback all transactions which are nested. Stored procedures can be both nested and on their own.
alter procedure TestErrors
as
begin
begin try
begin transaction
exec TestErrorsInner;
IF ##TRANCOUNT > 0
commit transaction;
end try
begin catch
IF ##TRANCOUNT > 0
rollback transaction;
select ERROR_MESSAGE();
end catch
end
alter procedure TestErrorsInner
as
begin
begin try
begin transaction
RAISERROR('Test Error',16,1);
IF ##TRANCOUNT > 0
commit transaction;
end try
begin catch
IF ##TRANCOUNT > 0
rollback transaction;
select ERROR_MESSAGE();
end catch
end
Results:
Test Error
Transaction count after EXECUTE indicates a mismatching number of
BEGIN and COMMIT statements. Previous count = 1, current count = 0.
This is because you are catching a transaction in the TestErrors which is not in Active state.
You have already rolled back your transaction in Catch block of TestErrorsInner.
Then again you are trying to do COMMIT/ROLLBACK it in TestErrors. So it is throwing an error.
It is your responsibility to Raise an Error explicitly again in Catch block of TestErrorsInner. So that Error will be the input for Parent SP.
So your TestErrorsInner should be like
ALTER PROCEDURE TESTERRORSINNER
AS
BEGIN
BEGIN TRY
BEGIN TRANSACTION
RAISERROR('TEST ERROR',16,1);
IF ##TRANCOUNT > 0
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION;
--SELECT ERROR_MESSAGE();
RAISERROR('TEST ERROR in Catch',16,1); --Here Raised
END CATCH
END
Now execute the TestErrors Stored procedure, you won't get that error.
And You can check the Transaction Status with XACT_STATE()
Calling XACT_STATE() will give result of 0 or 1 or -1 (From MSDN)
If 1, the transaction is committable.
If -1, the transaction is uncommittable and should be rolled back.
if XACT_STATE = 0 means there is no transaction and a commit or rollback operation would generate an error.

Rollback an entire stored procedure

I have a stored procedure with multiple update statements.I dont want to use try catch.How can I rollback the stored procedure and get back the original table?
can something like this work -
begin transaction t1
spName
rollback transaction t1
Yes you can wrap everything into a sproc into a transaction
begin tran
exec testproc
commit tran
--rollback tran --for condition
It works fine even for commit as well rollback
If for inside the sproc you need to open another transaction then you need to capture
DECLARE #vTranCount INT = ##TRANCOUNT
--Commit
IF (#vTranCount = 0 AND ##TRANCOUNT <> 0) COMMIT TRANSACTION --Commit if the Tran is created by this sproc
--rollback during catch
IF(#vTranCount = 0 AND ##TRANCOUNT > 0) ROLLBACK TRANSACTION --Rollback if the Tran is created by this sproc

RETURN inside a transaction with TRY-CATCH block

I have the following code, and I am wondering, as a generic example, if the transaction is left open if it exits with RETURN.
BEGIN TRANSACTION
BEGIN TRY
IF NOT EXISTS(SELECT 1 FROM dbo.tblProducts WHERE intProductID = #intProductID)
BEGIN
SELECT 'Product does not exists' AS strMessage
RETURN
END
UPDATE dbo.tblProducts SET
curPrice = 10
WHERE
intProductID = #intProductID
SELECT 'Success' AS strMessage
END TRY
BEGIN CATCH
SELECT ERROR_MESSAGE() AS strMessage
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION
END CATCH
IF ##TRANCOUNT > 0
COMMIT TRANSACTION
It should be like Below
BEGIN TRY
SET NOCOUNT ON
SET XACT_ABORT ON
BEGIN TRANSACTION
IF NOT EXISTS(SELECT 1 FROM dbo.tblProducts
WHERE intProductID = #intProductID)
BEGIN
SELECT 'Product does not exists' AS strMessage
Rollback TRan
RETURN
END
UPDATE dbo.tblProducts SET
curPrice = 10
WHERE
intProductID = #intProductID
SELECT 'Success' AS strMessage
COMMIT TRAN
END TRY
BEGIN CATCH
SELECT ERROR_MESSAGE() AS strMessage
ROLLBACK TRANSACTION
END CATCH
I just tried this by running the code above then checking SELECT ##TRANCOUNT and then by attempting a ROLLBACK. After the return, ##TRANCOUNT was 1 and I was able to rollback the transaction successfully indicating that the transaction is left open.
This code works with nested transactions:
BEGIN TRY
IF ##TRANCOUNT > 0
SAVE TRANSACTION MyTransactionName
ELSE
BEGIN TRANSACTION MyTransactionName
IF NOT EXISTS(SELECT 1 FROM dbo.tblProducts WHERE intProductID = #intProductID)
BEGIN
SELECT 'Product does not exists' AS strMessage
ROLLBACK TRANSACTION MyTransactionName
RETURN
END
UPDATE dbo.tblProducts SET
curPrice = 10
WHERE
intProductID = #intProductID
SELECT 'Success' AS strMessage
COMMIT TRANSACTION MyTransactionName
END TRY
BEGIN CATCH
SELECT ERROR_MESSAGE() AS strMessage
ROLLBACK TRANSACTION MyTransactionName
END CATCH
Please see the Stored Procedure template that I posted in the following DBA.StackExchange answer as it address the use of TRY / CATCH with Transactions, and it handles nested calls, even if the Transaction is opened in the app layer:
Are we required to handle Transaction in C# Code as well as in Store procedure

Tell SQL Server the error is "handled" in try...catch

I'd like to indicate to SQL Server 2005, in my BEGIN CATCH...END CATCH block that the error is "handled"... That is, clear the error.
Is that possible? Consider this:
begin transaction
begin try
begin transaction
select cast('X' as bit)
commit transaction
end try
begin catch rollback transaction
select error_number(), error_message()
end catch
commit transaction
This results in the following:
(0 row(s) affected)
(No column name) (No column name)
245 Conversion failed when converting the varchar value 'X' to data type bit.
(1 row(s) affected)
Msg 3902, Level 16, State 1, Line 13
The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.
Thanks.
A.
Not all errors are maskable. You are always supposed to inspect the XACT_STATE() and see if you can continue. Certain errors (1205 deadlock being a typical example) will rollback the transaction and not allow you to continue.
What you describe (a loop which can preserve the work) is ussualy done with the help of a savepoint:
begin transaction
begin try
while #loopcondition
begin
save transaction loop;
begin try
-- process loop element here
end try
begin catch
if xact_state() = -1
begin
-- whole transaction is doomed
rollback;
raiserror ('Aborting', ....);
end
else if xact_state() = 0
begin
-- trasaction was aborted by inner loop
raiserror ('Aborted inside', ....);
end
else if xact_state() = 1
begin
-- this error is recoverable, rollback to the savepoint and continue the loop
rollback loop
end
end catch
-- continue loop here
fetch next from ....
/*
-- batch commit here if batch committing
if #batchsize
begin
commit;
begin transaction
end
*/
end
commit;
end try
begin catch
-- if we get here, we could not handle the error inside the loop and continue
if xact_state() != 0
rollback
raiserror('failed to process', ...)
end catch