BEGIN TRY/CATCH and MSDTC error - sql-server

1/ The following code snippet show me the expected error: The INSERT statement conflicted with the FOREIGN KEY constraint FK_...
SET XACT_ABORT ON;
BEGIN TRANSACTION
INSERT INTO linkedsrv1.db1.[dbo].tbl1 ([Col1], [Col2])
VALUES (1200, 0)
COMMIT TRANSACTION
2/ But when I put this in a BEGIN TRY/CATCH, the error message is vague: Msg 1206, Level 18, State 118, Line 18
The Microsoft Distributed Transaction Coordinator (MS DTC) has cancelled the distributed transaction.
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRANSACTION
-- Error is on this line
INSERT INTO linkedsrv1.db1.[dbo].tbl1 ([IdWebsite], [IdProductType])
VALUES (1200, 0)
COMMIT TRANSACTION
END TRY
BEGIN CATCH
PRINT 'Error' -- Code not reached
SELECT ERROR_NUMBER(), ERROR_MESSAGE(), ERROR_SEVERITY(), ERROR_STATE()
IF XACT_STATE() != 0
ROLLBACK TRANSACTION
END CATCH
Any idea why this happens?
Later edit:
It works in case I remove the unneeded explicit transaction. It is still not clear why I get this error when I put BEGIN/COMMIT TRAN.
I get the same error in case I have multiple inserts in multiple tables situated on linked server.
Any comment / remark is welcomed.

From MSDN:
SYMPTOMS
Consider the following scenario. You use the SQL Native Client OLE DB provider (SQLNCLI) in SQL Server 2005 to create a linked server. You create a distributed transaction. The distributed transaction contains a query that uses the linked server to retrieve data from a table. When you commit the distributed transaction, you may receive the following error message:
Msg 1206, Level 18, State 167, Line 3
The Microsoft Distributed Transaction Coordinator (MS DTC) has cancelled
the distributed transaction.
Additionally, you may receive the following error message when you run a query after this behavior occurs:
Msg 8525, Level 16, State 1, Line 1
Distributed transaction completed. Either enlist this session in a new
transaction or the NULL transaction.
This problem occurs if the following conditions are true:
You use the SQLNCLI provider to create a linked server between two
instances of SQL Server 2005.
The XACT_ABORT option is set to ON.
In the distributed transaction, you try to release a rowset before
all rows in the rowset are processed.
Note This problem may also occur if you call the ReleaseRows method in a distributed transaction to release a rowset before you commit a distributed transaction in an application.
CAUSE
This problem occurs because the SQLNCLI provider incorrectly sends an attention signal to the linked server to roll back the distributed transaction.
WORKAROUND
To prevent the SQLNCLI provider from sending an attention signal to the server, use the SQLNCLI provider to consume fully any rowsets that the OLE DB consumer creates.
Update
you need to configure 'remote proc trans' to "1" in server parameters.
Ex:
exec sp_configure 'remote proc trans','1'
reconfigure with override
This will permmit you to execute any distributed queries.
More Update
If you are using .Net framework in front end too, then I think you can use
TransactionScope Class. Remove transaction from query and put the Transaction in code level.

I have went to through this pain!
If you are performing any CRUD operation on a single table TRANSACTION is not needed.
In this case, the problem is, XACT_STATE() returns -1 because there is an error in the active transaction. But, ROLLBACK TRANSACTION fails, since there is NO transactions happened. You did only one transaction, INSERT, which failed, so there are no other transactions to rollback.
Its always better to relay on ##TRANCOUNT than XACT_STATE() (at least in this case).
to make it work, change like this(though I don't support TRAN for single table):
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION

BEGIN TRANSACTION starts a distributed transaction between the server running the statements and the linked server, since potentially you can run updates against both servers. When the INSERT fails it needs to cancel the distributed transaction, thus the error you are getting. So you have to handle errors on two levels (insert and transaction). In this scenario, you'll need two TRY/CATCH blocks as follows:
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRANSACTION
BEGIN TRY
-- Error is on this line
INSERT INTO linkedsrv1.db1.[dbo].tbl1 ([IdWebsite], [IdProductType])
VALUES (1200, 0)
END TRY
BEGIN CATCH
SELECT 'Insert Error', ERROR_NUMBER(), ERROR_MESSAGE(), ERROR_SEVERITY(), ERROR_STATE()
RAISERROR (15600,-1,-1, 'INSERT ERROR');
END CATCH
COMMIT TRANSACTION
END TRY
BEGIN CATCH
SELECT 'Transaction Error', ERROR_NUMBER(), ERROR_MESSAGE(), ERROR_SEVERITY(), ERROR_STATE()
IF XACT_STATE() != 0
ROLLBACK TRANSACTION
END CATCH

Related

SQL Server transactions errors due to different Isolation Levels

I created the following stored procedure in order to examine the isolation level behavior in transaction:
CREATE PROCEDURE ReadCommittedIsolationLevel
AS
BEGIN
BEGIN TRANSACTION t1
BEGIN TRY
EXEC SnapShotIsolationLevel
COMMIT TRANSACTION
END TRY
BEGIN CATCH
PRINT ERROR_MESSAGE()
ROLLBACK TRANSACTION t1
END CATCH
END
CREATE PROCEDURE SnapShotIsolationLevel
AS
BEGIN
SET TRANSACTION ISOLATION LEVEL SNAPSHOT
BEGIN TRANSACTION t2
BEGIN TRY
SELECT TOP 20 *
FROM Orders
ORDER BY 1 DESC
COMMIT
END TRY
BEGIN CATCH
PRINT ERROR_MESSAGE()
ROLLBACK TRAN t2
END CATCH
END
And then I run this:
EXEC ReadCommittedIsolationLevel
I get this error:
Transaction failed in database 'MyDataBase' because the statement was run under snapshot isolation but the transaction did not start in snapshot isolation. You cannot change the isolation level of the transaction to snapshot after the transaction has started unless the transaction was originally started under snapshot isolation level.
Cannot roll back t2. No transaction or savepoint of that name was found.
If I remove the transactions and run it like plain stored procedures, it works fine.
Why is that?
The error gave you the correct explanation:
You cannot change the isolation level of the transaction to snapshot
after the transaction has started.
SQL Server does not have nested transactions, the only real transaction you have is the outer transaction started under Read Committed, the next begin tran does nothing except for incrementing ##trancount. You can read more on it here: A SQL Server DBA myth a day: (26/30) nested transactions are real by Paul Randal
Snapshot Isolation means consistent data at the transaction level, not at the statement level(RCSI), so you should change Isolation level to Snapshot at the beginning of the transaction or you cannot change it to Snapshot at all.

Difference between batch and transaction in SQL Server in terms of handling errors

I am having a bit of hard time understanding how errors affect the completion of batches and/or transactions.
For instance:
BEGIN TRAN;
SELECT 1/0 AS Error;
ROLLBACK;
BEGIN TRAN;
SELECT 1/1 AS NOError;
COMMIT;
GO
Should not the second transaction succeed even though the first fails? Are not transactions dealt with on one-by-one basis? And what is the role played by batches here?
I was reading about SET XACT_ABORT ON command, and the MSDN says:
When SET XACT_ABORT is ON, if a Transact-SQL statement raises a run-time error, the entire transaction is terminated and rolled back.
If it only fails the containing transaction, why the second transaction is never reached?

Roll back in SQL Server 2005

My question: Is it possible to rollback a stored procedure from another stored procedure in SQL Server 2005?
I have SP1 to insert the values into one table and SP2 to insert the values into another table.
So, if any error comes while executing the SP2 I want to rollback the SP1 also.
Please anyone help me to solve my problem.
Thanks,
Bharath
You need to wrap both calls in a single transaction.
if you call them in SQL then this is way, or using a more comprehensive version like the other answer by answered 7 mins ago gbn.
create proc doall as
BEGIN TRY
BEGIN TRAN
EXECUTE SP1
EXECUTE SP1
COMMIT TRAN
END TRY
BEGIN CATCH
ROLLBACK TRAN
END CATCH;
if you're calling the SPs from another source, such as from a non SQL program, you need to setup the outer transaction using a Microsoft Distributed Transaction Coordinator (MSDTC) service.
Depending on the API you're using you set up the transaction in Code, and then commit and rollback in code, dependant on conditions.
for example in .net you can use the System.Transactions namespace to create distributed transactions.
In the main program
var tran = new System.Transactions.Transaction();
.
.
.
in one piece of code doe a db call (and pass the tran object to the sql connection) so it enlists in the transaction... if it fails - abort the transaction (trans.Rollback())
.
.
.
.
in another piece of code do another db call (and pass the tran object to the sql connection) so it enlists in the transaction... if it fails - abort the transaction (trans.Rollback())
.
.
.
later...
if both pieces of code succeed commit the transaction
This is a good introduction to the this namespace if you're using .net
You need a wrapper stored procedure to manage the transaction.
##TRANCOUNT on entry and exit to a stored procedure must be the same, otherwise you get error 266. So you can't exit SP1 having started a TXN for example.
I assume that SP1 and SP2 ca nbe called standalone so you need a nested transaction.
Then you hit the same error because
BEGIN TRAN adds one to ##TRANCOUNT
COMMIT TRAN subtracts one from ##TRANCOUNT
ROLLBACK makes ##TRANCOUNT zero
So you can still get error 266.
My answer here explains more about it, including nesting, error 266 suppression etc: Nested stored procedures containing TRY CATCH ROLLBACK pattern?
So in your case, you need soemthing like this
CREATE PROCEDURE Wrapper
AS
SET XACT_ABORT, NOCOUNT ON
DECLARE #starttrancount int
BEGIN TRY
SELECT #starttrancount = ##TRANCOUNT
IF #starttrancount = 0
BEGIN TRANSACTION
EXEC SP1
EXEC SP2
IF #starttrancount = 0
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0 AND #starttrancount = 0
ROLLBACK TRANSACTION
RAISERROR [rethrow caught error using #ErrorNumber, #ErrorMessage, etc]
END CATCH
GO

The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION

Here's a strange problem I'm running into on a production server. It has happened twice in the last two weeks, and this is a server that gets a lot of traffic.
We have some code in a Web Service that executes a BEGIN TRAN, then runs a few SQL queries (two inserts followed by an update). Then at the end executes a COMMIT. Twice now we have gotten the message in the logs:
The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.
Between the first two inserts and the update, we call another web service, so there could be a slight delay between the first two inserts and last update before the COMMIT is called. Could this be causing our problem? We're running this on IIS 7 and Server 2008 R2 (all updated applied).
Originally we though it could be the app pools getting recycled, but changed that to recycle in the middle of the night. Now I'm not sure what would be causing SQL server to forget the call to BEGIN TRAN.
This web service does get called quite a bit. Has anyone seen something like this before? I'm at a total loss at the moment...
Any help or suggestion appreciated greatly!
It looks like your transaction failed, got rolled back and there is nothing to commit
example of such a thing
CREATE TABLE BlaTest(id INT PRIMARY KEY NOT NULL)
GO
Now run this
BEGIN TRAN
INSERT BlaTest VALUES('a')
GO
COMMIT TRAN
Here is the error
Msg 245, Level 16, State 1, Line 3
Conversion failed when converting the varchar value 'a' to data type int.
Msg 3902, Level 16, State 1, Line 2
The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.
This will run without a problem
BEGIN TRAN
INSERT BlaTest VALUES(5)
GO
COMMIT TRAN
A good article on transactions is Error Handling in SQL 2005 and Later by Erland Sommarskog
My issue was I needed a BEGIN and END around my BEGIN TRAN and COMMIT TRAN.
BEGIN
BEGIN TRAN
INSERT BlaTest VALUES(5)
GO
COMMIT TRAN
END
BEGIN TRANS
at the top will help
I had the same issue. This is what I did to solve it.
The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION
After I Checked the SQL Query and Add a BEGIN TRAN it will executed successfully. Here My sample code. It will work:
ALTER procedure [dbo].[DeactivateUser]
#UserId bigint,
#LoginEmail Nvarchar(100),
#merchantId int
as
Begin
Begin tran
update Users set
LoginEmail='inactive'+CONVERT(VARCHAR(11), getdate(), 106)+'-'+#LoginEmail,
IsActive=0
where LoginEmail=#LoginEmail and MerchantID=#merchantId
if(##ERROR=0)
begin
commit Tran
select 0
end
else
begin
rollback Tran
select -1
end
end

Check Contraint Bypassing CATCH block in Distributed Transaction

I have a MSSSQL stored procedure performing a distributed transaction that looks like this:
SET XACT_ABORT ON;
SET NOCOUNT ON;
BEGIN TRY
BEGIN DISTRIBUTED TRANSACTION
insert into LNKSRV.INST.dbo.zz (id, val) values (1, 'a');
insert into LNKSRV.INST.dbo.zz (id, val) values (2, 'b');
COMMIT TRANSACTION
END TRY
BEGIN CATCH
if (XACT_STATE() <> 0)
BEGIN
ROLLBACK TRANSACTION;
END
print ERROR_MESSAGE();
print ERROR_LINE();
print ERROR_SEVERITY();
END CATCH
This works fine.
If I add this 3rd insert statement:
insert into LNKSRV.INST.dbo.zz (id, val) values ('error', 'b');
...it fails correctly -- the transaction is rolled back on the remote server and control passes to the CATCH block and I get information about the error (can't convert 'error' to int).
But if I add this insert statement:
insert into LNKSRV.INST.dbo.zz (id, val) values (-1, 'b');
..and I have a check contraint on the remote table requiring values > 0 in the id column, then things do not work as I expect. The transaction DOES roll back, but control DOES NOT transfer to the catch block. Instead, execution just dies and this is printed to the output window:
The Microsoft Distributed Transaction Coordinator (MS DTC) has cancelled the distributed transaction
Why? I need to log these errors in the catch blog.
Since the distributed transaction coordinator is handling this, when the transaction fails on the distributed part of the transaction, the DTC sends a message in the form of an attention, which stops your code from executing, and which the TRY/CATCH cannot process.
SQL Server can detect on your end when you are trying to insert an incorrect data type into a table (even on a remote instance) but the constraint is processed on the linked server, which causes the attention to be sent to DTC and your TRY/CATCH to be ignored.
For more information see the first "Note" section in the "Using TRY...CATCH in Transact-SQL" section of SQL Server 2008 Books Online, located at:
http://msdn.microsoft.com/en-us/library/ms179296.aspx

Resources