我曾以为这些方法已经被其他人测试过了,尤其是根据一些评论。但我的测试表明,即使通过.NET SqlClient
连接,这两种方法确实在数据库层面上起作用。这已经被其他人测试和验证过了。
服务器范围内
您可以将user options服务器配置设置为当前它与64(ARITHABORT
的值)按位OR
的任何内容。如果您不使用按位 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;");
SET ARITHABORT ON;
。这种方法的缺点是它会针对每个查询执行一次,除非您存储一个本地变量来捕获是否已执行的状态,并在每次测试时检查(这实际上并不需要太多额外工作,但使用ExecuteSqlCommand
可能更容易)。
无论哪种方法都可以让您在一个地方处理此事,而不需要更改任何现有的代码。
另外,您还可以创建一个类似于以下方法的包装方法来实现此功能:
public static SqlDataReader ExecuteReaderWithSetting(SqlCommand CommandToExec)
{
CommandToExec.CommandText = "SET ARITHABORT ON;\n" + CommandToExec.CommandText;
return CommandToExec.ExecuteReader();
}
然后只需将当前的_Reader = _Command.ExecuteReader();
引用更改为_Reader = ExecuteReaderWithSetting(_Command);
。
这样做还可以在一个位置处理设置,同时只需要进行最小和简单的代码更改,大部分可以通过查找和替换完成。
更好的是(另外第二部分),由于这是一个连接级别的设置,它不需要在每个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的类来实现。
或者更好的方法(另外第三部分),您可以为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;
:
除了Sankar的解决方案之外,将服务器级别上的算术中止设置应用于所有连接也可以起作用:
EXEC sys.sp_configure N'user options', N'64'
GO
RECONFIGURE WITH OVERRIDE
GO
从 SQL 2014 起,对于所有连接都应该设置为 开启:
您应始终将 ARITHABORT 设置为 ON 在您的登录会话中。设置 ARITHABORT 为 OFF 可能会对查询优化产生负面影响,导致性能问题。
因此,这似乎是理想的解决方案。
如果选项1不可行,并且您在大多数 SQL 查询中使用存储过程(建议如此,请参阅存储过程 vs. 内联SQL),则只需在每个相关存储过程中启用该选项:
CREATE PROCEDURE ...
AS
BEGIN
SET ARITHABORT ON
SELECT ...
END
GO
我相信这里最好的真正解决方案就是简单地编辑您的代码,因为它是错误的,任何其他修复方法只不过是权宜之计。
set ArithAbort off
开始的。我希望能在 .net / C# 方面做些什么。我设置了赏金,因为我看到了这个建议。 - Henrik Staun PoulsenString sConnectionstring;
sConnectionstring = "Initial Catalog=Pubs;Integrated Security=true;Data Source=DCC2516";
SqlConnection Conn = new SqlConnection(sConnectionstring);
SqlCommand blah = new SqlCommand("SET ARITHABORT ON", Conn);
blah.ExecuteNonQuery();
SqlCommand cmd = new SqlCommand();
// Int32 rowsAffected;
cmd.CommandText = "dbo.xmltext_import";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = Conn;
Conn.Open();
//Console.Write ("Connection is open");
//rowsAffected =
cmd.ExecuteNonQuery();
Conn.Close();
参考:http://social.msdn.microsoft.com/Forums/en-US/transactsql/thread/d9e3e8ba-4948-4419-bb6b-dd5208bd7547/
没有设置可以强制 SqlClient 始终设置 ARITHABORT,您必须按照您所描述的方式进行设置。
有趣的是,根据Microsoft 关于 SET ARITHABORT 的文档:
您应该始终在登录会话中将 ARITHABORT 设置为 ON。将 ARITHABORT 设置为 OFF 可能会对查询优化产生负面影响,导致性能问题。
然而,.Net 连接默认情况下硬编码为关闭此设置?
另外,当诊断此设置时,您必须非常小心。不同的设置选项将导致相同查询的不同查询计划。您的 .Net 代码可能会遇到性能问题(SET ARITHABORT OFF),但当您在 SSMS 中运行相同的 TSQL 查询(默认情况下 SET ARITHABORT ON)时,可能会正常工作。这是因为 .Net 查询计划不会被重用,而是生成一个新的计划。这可能会消除参数嗅探问题,并提供更好的性能。
ANSI_WARNINGS
已经被默认设置为打开,并且像索引视图之类的功能也可以正常工作。 - Martin Smith如果可以节省任何人的时间,在我的情况下(Entity Framework Core 2.0.3,ASP.Net Core API,SQL Server 2008 R2):
user_options
对我来说都是不可接受的(它们确实有效 - 我已经测试过),但我不能冒着影响其他应用程序的风险。从EF Core进行的即席查询,在顶部加上SET ARITHABORT ON;
是行不通的。
最后,对我有效的解决方案是:将一个存储过程与在EXEC
之前用分号分隔的SET
选项一起作为原始查询调用,就像这样:
// C# EF Core
int result = _context.Database.ExecuteSqlCommand($@"
SET ARITHABORT ON;
EXEC MyUpdateTableStoredProc
@Param1 = {value1}
");
在Solomon Rutzy的回答的基础上,针对EF6进行扩展:
using System.Data;
using System.Data.Common;
namespace project.Data.Models
{
abstract class ProjectDBContextBase: DbContext
{
internal ProjectDBContextBase(string nameOrConnectionString) : base(nameOrConnectionString)
{
this.Database.Connection.StateChange += new StateChangeEventHandler(OnStateChange);
}
protected static void OnStateChange(object sender, StateChangeEventArgs args)
{
if (args.OriginalState == ConnectionState.Closed
&& args.CurrentState == ConnectionState.Open)
{
using (DbCommand _Command = ((DbConnection)sender).CreateCommand())
{
_Command.CommandType = CommandType.Text;
_Command.CommandText = "SET ARITHABORT ON;";
_Command.ExecuteNonQuery();
}
}
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
...
这里使用的是System.Data.Common
的DbCommand
而不是SqlCommand
,以及DbConnection
而不是SqlConnection
。
一个SQL Profiler跟踪确认,在连接打开之前发送了SET ARITHABORT ON
,在事务中执行任何其他命令之前。
SELECT DATABASEPROPERTYEX('{database_name}', 'IsArithmeticAbortEnabled');
返回1,但是sys.dm_exec_sessions显示arithabort关闭,尽管在Profiler中没有看到任何明确的SET语句。为什么会这样呢? - andrew.rockwellARITHABORT
是一个会话级别的设置,而不是每个查询。如果在连接时对第一个查询起作用,那已经是与事件处理程序执行的SET
查询相关的后续查询了。然而,我突然想到连接池可能是个问题。当从连接池中取出连接时,sp_reset_connection会被执行,并将会话级别的设置重置为默认值。我不确定从连接池中取出连接是否会触发这个事件处理程序。如果是这样的话,那么我认为这应该是可靠的。 - Solomon RutzkySqlConnection.Open()
包装器)仍然可以保证工作,所以可能是更好的选择,对吗? - Solomon RutzkyDbContext
的重新生成不会覆盖你的更改,那么这将是可行且优先于 Opt3 的选择,我个人认为如此。但是,在我的使用案例中,我选择冒险使用了 Opt3(没有心情覆盖连接开放,并且我觉得竞争条件非常罕见,如果存在的话)。对于 Opt3,我非常感激你;) - Shockwaver