I have a SQL Linked Server setup on server #1 to access server #2. When I run an update or insert statement in SSMS on server #1 that changes data in server #2 it works. Now I use the exact same UPDATE statement in a trigger on a table (on server #1), and then it fails with the error:
OLE DB provider "SQLNCLI11" for linked server "172.xx.xx.02" returned message "No transaction is active.". Msg 7391, Level 16, State 2, Procedure MyProc, Line 12 The operation could not be performed because OLE DB provider "SQLNCLI11" for linked server "172.xx.xx.02" was unable to begin a distributed transaction.
Since a normal UPDATE/INSERT is working, I'm going to assume the DTC is setup correctly and working (or is this a wrong assumption?). As almost all the help on the web refers to DTC, I'm pasting my DTC settings below anyway. Anything else that it can be? (and before anyone critiques Linked Servers, yes I know it's old and not the preferred way, but can't change it now - it's a legacy system).
My Trigger looks like this:
ALTER TRIGGER [dbo].[MyTrigger]
ON [dbo].[Table1]
FOR UPDATE, INSERT
AS
BEGIN
INSERT INTO [172.xx.xx.02].SomeDB.dbo.MyTable (Field1, Field2)
VALUES ('34172', GETDATE())
END
Linked Server Properties:[
]1
DTC:
EDIT: Although I'm still looking for answers and will assign the bounty to any correct answer, I did find this work-around as a temporary solution: I can change the trigger to run a SQL statement directly on the remote server, thereby bypassing the DTC.
CREATE TRIGGER [dbo].[MyTrigger]
ON [dbo].[MyTable]
FOR UPDATE, INSERT
AS
BEGIN
-- Perform the update on the linked server without distributed transaction
DECLARE @sql NVARCHAR(MAX);
SET @sql = N'
INSERT INTO [172.1.1.10].SomeDB.dbo.MyTable (Col1, Col2)
VALUES (''ABC'', GETDATE()';
-- Execute the update using sp_executesql
EXEC [172.1.1.10].master.sys.sp_executesql
@sql;
END;

"
was unable to begin a distributed transaction":If the "
Enable Promotion of Distributed Transactions" is set to False in the linked server properties, it might be the cause of the issue. When set to false, it means that Microsoft SQL Server will not use Microsoft Distributed Transaction Coordinator (MS DTC) to coordinate transactions that are distributed across multiple instances of SQL Server.Try changing "Enable Promotion of Distributed Transactions" to True.
You might need to restart the SQL service for the changes to take effect.
Besides that, try and follow the official Microsoft page: "Troubleshooting Problems with MSDTC".
If changing the "
Enable Promotion of Distributed Transactions" toTruedid not resolve the issue, and the DTC appears to be set up correctly, there might be other reasons for the behavior.Triggers, by their nature, execute within the scope of the action that fired them. If the outer operation (the INSERT or UPDATE that fires the trigger) is already within a transaction, and then the trigger tries to perform a distributed operation, this could be problematic.
Consider whether the original operation is wrapped in a transaction and if that might be the cause.
And make sure the account SQL Server is running under on both Server #1 and Server #2 has adequate permissions to initiate a distributed transaction.
If the trigger is affecting multiple databases (even on the same server), SQL Server might be promoting the transaction to a distributed transaction. If any of these databases are read-only or have some other constraints, it could cause the error.
Alternative Approaches, as a workaround (not necessarily a direct solution)
Your workaround leverages the
sp_executesqlstored procedure to execute the SQL statement on the remote server. That approach effectively decouples the operations, bypassing the Distributed Transaction Coordinator (DTC) and preventing it from promoting the operation to a distributed transaction.But you could also:
INSERTorUPDATE, there might be scenarios where multiple rows are affected. Make sure your dynamic SQL handles all necessary rows.TRY-CATCHblock to capture and handle any errors that might arise during the execution of the remote SQL.On the last point:
Inside the
CATCHblock, the error details are captured using theERROR_MESSAGE(),ERROR_SEVERITY(), andERROR_STATE()functions.Then, for demonstration purposes, I have used the
RAISERRORfunction to raise the error, but you can replace that with logging or other error-handling actions as appropriate for your environment.As an alternative, you could try and a void direct remote execution in trigger:
In SQL Server Management Studio, make sure "Implicit Transactions" is turned off. If turned on, every query you run is automatically wrapped in a transaction. You can toggle it off with:
And make sure that the option “Enable Promotion of Distributed Transaction” for the linked server is indeed set to
False. This setting is supposed to prevent SQL Server from trying to start a distributed transaction.