使 SqlClient 預設為 ARITHABORT ON
首先要做的事情:我使用 MS SQL Server 2008 和兼容級別為 80 的數據庫,並使用 .Net 連接到它
System.Data.SqlClient.SqlConnection
。出於性能原因,我創建了一個索引視圖。因此,視圖中引用的表的更新需要使用
ARITHABORT ON
. 但是,分析器顯示 SqlClient 正在與 連接ARITHABORT OFF
,因此對這些表的更新失敗。是否有讓 SqlClient 使用的中央配置設置
ARITHABORT ON
?我能找到的最好的方法是每次打開連接時手動執行,但是更新現有的程式碼庫來做到這一點將是一項相當大的任務,所以我很想找到更好的方法。
看似首選的方法
我的印像是以下內容已經被其他人測試過,特別是基於一些評論。但我的測試表明,這兩種方法確實在數據庫級別有效,即使通過 .NET 連接也是如此
SqlClient
。這些已經過其他人的測試和驗證。伺服器範圍
您可以將使用者選項伺服器配置設置設置為目前按位
OR
編輯的 64(的值ARITHABORT
)。如果您不使用按位 OR (|
) 而是執行直接賦值 (=
),那麼您將清除已啟用的任何其他現有選項。DECLARE @Value INT; SELECT @Value = CONVERT(INT, [value_in_use]) --[config_value] | 64 FROM sys.configurations sc WHERE sc.[name] = N'user options'; IF ((@Value & 64) <> 64) BEGIN PRINT 'Enabling ARITHABORT...'; SET @Value = (@Value | 64); EXEC sp_configure N'user options', @Value; RECONFIGURE; END; EXEC sp_configure N'user options'; -- verify current state
數據庫級
這可以通過ALTER DATABASE SET為每個數據庫設置:
USE [master]; IF (EXISTS( SELECT * FROM sys.databases db WHERE db.[name] = N'{database_name}' AND db.[is_arithabort_on] = 0 )) BEGIN PRINT 'Enabling ARITHABORT...'; ALTER DATABASE [{database_name}] SET ARITHABORT ON WITH NO_WAIT; END;
替代方法
不太好的消息是我在這個主題上做了很多搜尋,結果發現多年來很多其他人在這個主題上做了很多搜尋,並且無法配置行為的
SqlClient
。一些 MSDN 文件暗示它可以通過 ConnectionString 來完成,但是沒有允許更改這些設置的關鍵字。另一個文件暗示它可以通過客戶端網路配置/配置管理器進行更改,但這似乎也不可能。因此,不幸的是,您將需要SET ARITHABORT ON;
手動執行。以下是一些需要考慮的方法:如果您使用的是 Entity Framework 6(或更新版本),您可以嘗試:
- 使用Database.ExecuteSqlCommand:
context.Database.ExecuteSqlCommand("SET ARITHABORT ON;");
理想情況下,這將在打開數據庫連接後執行一次,而不是每次查詢。
通過以下任一方式創建攔截器:
- DbConfiguration.AddInterceptor
- DbInterception.Add這將允許您在執行 SQL 之前對其進行修改,在這種情況下,您可以簡單地為其添加前綴:
SET ARITHABORT ON;
。這裡的缺點是它將針對每個查詢,除非您儲存一個局部變數來擷取它是否已執行並每次測試的狀態(這實際上並沒有那麼多額外的工作,但使用ExecuteSqlCommand
is可能更容易)。其中任何一個都可以讓您在一個地方處理此問題,而無需更改任何現有程式碼。
ELSE,您可以創建一個執行此操作的包裝器方法,類似於:
public static SqlDataReader ExecuteReaderWithSetting(SqlCommand CommandToExec) { CommandToExec.CommandText = "SET ARITHABORT ON;\n" + CommandToExec.CommandText; return CommandToExec.ExecuteReader(); }
然後只需將目前
_Reader = _Command.ExecuteReader();
引用更改為_Reader = ExecuteReaderWithSetting(_Command);
.這樣做還允許在單個位置處理設置,同時只需要很少和簡單的程式碼更改,這些更改主要可以通過查找和替換來完成。
更好的是(其他第 2 部分),因為這是一個連接級別設置,它不需要在每個 SqlCommand.Execute__() 呼叫中執行。因此,不要為 創建一個包裝器
ExecuteReader()
,而是為 : 創建一個包裝器Connection.Open()
:public static void OpenAndSetArithAbort(SqlConnection MyConnection) { using (SqlCommand _Command = MyConnection.CreateCommand()) { _Command.CommandType = CommandType.Text; _Command.CommandText = "SET ARITHABORT ON;"; MyConnection.Open(); _Command.ExecuteNonQuery(); } return; }
然後只需將現有
_Connection.Open();
引用替換為OpenAndSetArithAbort(_Connection);
.通過創建一個擴展 SqlCommand 或 SqlConnection 的類,上述兩個想法都可以以更多的 OO 風格實現。
或者更好的是(其他第 3 部分),您可以為 Connection StateChange 創建一個事件處理程序,並讓它在連接更改時設置屬性
Closed
,Open
如下所示:protected static void OnStateChange(object sender, StateChangeEventArgs args) { if (args.OriginalState == ConnectionState.Closed && args.CurrentState == ConnectionState.Open) { using (SqlCommand _Command = ((SqlConnection)sender).CreateCommand()) { _Command.CommandType = CommandType.Text; _Command.CommandText = "SET ARITHABORT ON;"; _Command.ExecuteNonQuery(); } } }
有了這些,您只需將以下內容添加到您創建
SqlConnection
實例的每個位置:_Connection.StateChange += new StateChangeEventHandler(OnStateChange);
無需更改現有程式碼。我剛剛在一個小型控制台應用程序中嘗試了這種方法,通過列印
SELECT SESSIONPROPERTY('ARITHABORT');
. 它返回1
,但如果我禁用事件處理程序,它返回0
。為了完整起見,這裡有一些不起作用的東西(根本不工作或不那麼有效):
登錄觸發器:觸發器,即使在同一個會話中執行,即使在顯式啟動的事務中執行,仍然是一個子程序,因此它的設置(
SET
命令、本地臨時表等)是本地的並且不會存在該子過程的結束。添加
SET ARITHABORT ON;
到每個儲存過程的開頭:
- 這需要對現有項目進行大量工作,尤其是隨著儲存過程數量的增加
- 這無助於臨時查詢
選項1
除了Sankar 的解決方案,在伺服器級別為所有連接設置算術中止設置將起作用:
EXEC sys.sp_configure N'user options', N'64' GO RECONFIGURE WITH OVERRIDE GO
從 SQL 2014 開始,建議對所有連接啟用:
您應該始終在登錄會話中將 ARITHABORT 設置為 ON。將 ARITHABORT 設置為 OFF 會對查詢優化產生負面影響,從而導致性能問題。
所以這似乎是理想的解決方案。
選項 2
如果選項 1 不可行並且您對大多數 SQL 呼叫使用儲存過程(您應該,請參閱儲存過程與內聯 SQL),那麼只需在每個相關儲存過程中啟用該選項:
CREATE PROCEDURE ... AS BEGIN SET ARITHABORT ON SELECT ... END GO
我相信這裡最好的真正解決方案是簡單地編輯您的程式碼,因為它是錯誤的,任何其他修復都只是一種解決方法。