Linked Server Insert Into With Exec 發生無法分散式交易的錯誤

某日有同仁表示自己撰寫的一張報表有時執行成功,但有時候又會失敗,然而他將該TSQL語法直接在SSMS執行時卻都不會失敗。

失敗原因是 OLE DB 提供者 “MSOLEDBSQL” 無法開始分散式交易。看到這錯誤訊息及他貼給我的SQL語法後,我直覺先將該Linked Server的remote proc transaction promotion設為False,然後再請他測試。他測試後表示錯誤還是一樣(類似下圖狀況,用SSMS模擬)。

這狀況就有點奇特,該語法他也沒開交易然後remote proc transaction promotion我也關了,為何SQL Server還是會自己啟用分散式交易呢?但該語法在SSMS執行卻不會報錯(如下圖),只在AP執行會產出該錯誤訊息。

 

因此我只好開啟Profiler後請他在AP執行該報表來查查前端到底丟了那些語法過來。在Profiler中看見Login訊息中顯示該Session的預設ISOLATION LEVEL居然是SERIALIZABLE而不是SQL預設的READ COMMITTED

 

因此我在SSMS中將ISOLATION LEVEL改為SERIALIZABLE後再執行該語法,果然產出一樣的錯誤。因此可以推斷交易層級升級導致利用Insert Into #tmp Exec(@SQL)方式拉資料時會讓SQL Server背地啟用分散式交易

至於為何Login會是SERIALIZABLE是因為前端EF底層在啟用交易時如沒指定交易層級時,預設會使用SERIALIZABLE。再請同仁設定EF開交易時改用READ COMMITTED後,這問題就解決了。

由於吃過蠻多次Linked Server的虧,後續我將remote proc transaction promotion及ISOLATION LEVEL跟Transaction當成3個變數,然後根據各組合模式來測試SQL Server是否會啟用分散式交易,語法如下。

--變數一:transaction promotion
--EXEC master.dbo.sp_serveroption 
--@server=N'Srv', 
--@optname=N'remote proc transaction promotion', 
--@optvalue=N'true'


--變數二:隔離層級
--SET TRANSACTION ISOLATION LEVEL Read Committed
--SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

--變數三:啟用交易
BEGIN TRANSACTION;
	Declare @SQL Varchar(4000);
	Drop Table If Exists #tb1;
	Create Table #tb1(id int,cname nvarchar(10));
	SET @SQL='SELECT * From Ext.dbo.tb1';
	SET @SQL='SELECT * FROM OPENQUERY([Srv], ''' + REPLACE(@SQL, '''', '''''') + ''')';
	--INSERT INTO #tb1 EXEC(@SQL);
	--INSERT INTO #tb1 SELECT * FROM OPENQUERY([Srv],'SELECT * From Ext.dbo.tb1');
	--EXECUTE sp_executesql N'INSERT INTO #tb1 SELECT * FROM OPENQUERY([Srv],''SELECT * From Ext.dbo.tb1'')';
	--INSERT INTO #tb1 EXECUTE sp_executesql N'SELECT * FROM OPENQUERY([Srv],''SELECT * From Ext.dbo.tb1'')';
	EXECUTE [Srv].[Ext].[dbo].[sp1];
	Select * From #tb1;
COMMIT;

 

測試結果如下圖:

 remote proc 
transaction promotion
Transaction Isolation LevelBegin TransactionINSERT INTO #tb1 
EXEC(@SQL)
INSERT INTO #tb1 
SELECT * FROM 
OPENQUERY
EXECUTE 
sp_executesql
INSERT INTO #tb1 
EXECUTE sp_executesql
EXECUTE 
[Srv].[DB].dbo.sp1
1TRUERead CommittedYesYYYYN(無法分散式交易)
2TRUERead CommittedNoYYYYY
3TRUESERIALIZABLEYesN(無法分散式交易)N(無法分散式交易)N(無法分散式交易)N(無法分散式交易)N(無法分散式交易)
4TRUESERIALIZABLENoN(無法分散式交易)YYN(無法分散式交易)Y
5FALSERead CommittedYesYYYYY
6FALSERead CommittedNoYYYYY
7FALSESERIALIZABLEYesN(無法分散式交易)N(無法分散式交易)N(無法分散式交易)N(無法分散式交易)Y
8FALSESERIALIZABLENoN(無法分散式交易)YYN(無法分散式交易)Y

我是ROCK

rockchang@mails.fju.edu.tw