首先,第一件事:我将MS SQL Server 2008与兼容级别为80的数据库一起使用,并使用.Net的System.Data.SqlClient.SqlConnection连接到该数据库。出于性能原因,我创建了索引视图。结果,需要使用ARITHABORT ON对视图中引用的表进行更新。但是,探查器显示SqlClient正在与ARITHABORT OFF连接,因此对这些表的更新失败。

是否存在中央配置设置,以使SqlClient使用ARITHABORT ON?我能找到的最好的方法是在每次打开连接时手动执行该操作,但是更新现有代码库来执行此操作将是一个相当大的任务,因此我渴望找到一种更好的方法。 >

#1 楼

看似首选的方法

我觉得以下内容已经由其他人进行了测试,尤其是根据一些评论。但是我的测试表明,即使通过.NET SqlClient进行连接,这两种方法确实也可以在数据库级别上工作。这些已经过其他人的测试和验证。

服务器范围内的

您可以将用户选项服务器配置设置设置为当前按位进行的任意设置OR ed with 64 (ARITHABORT的值)。如果不使用按位或(|),而是执行直接分配(=),则将清除所有已启用的其他现有选项。

 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:

< pre class =“ lang-sql prettyprint-override”> 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(或更高版本),则可以尝试以下任一种:

理想情况下,这将在打开DB连接之后执行一次,而不是针对每个查询执行一次。

通过以下任一方式创建拦截器:


DbConfiguration .AddInterceptor
DbInterception.Add

这将允许您在执行SQL之前对其进行修改,在这种情况下,您可以简单地为其添加前缀:context.Database.ExecuteSqlCommand("SET ARITHABORT ON;");。这样做的弊端是每次查询都会用到,除非您存储一个局部变量以捕获它是否已执行并每次进行测试的状态(这确实不是很多额外的工作,但是使用SET ARITHABORT ON;可能会更容易。)


这两种方法都可以让您一站式处理而无需更改任何现有代码。

ELSE,您可以创建包装器执行此操作的方法,类似于:

public static SqlDataReader ExecuteReaderWithSetting(SqlCommand CommandToExec)
{
  CommandToExec.CommandText = "SET ARITHABORT ON;\n" + CommandToExec.CommandText;

  return CommandToExec.ExecuteReader();
}


,然后只需将当前ExecuteSqlCommand引用更改为_Reader = _Command.ExecuteReader();即可。

这样做还允许该设置将在单个位置进行处理,而只需要进行最少且简单的代码更改即可,通常可以通过“查找并替换”完成。

更好(第2部分),因为这是连接级别设置,不需要每次SqlCommand.Execute __()调用都执行它。因此,与其为_Reader = ExecuteReaderWithSetting(_Command);创建包装器,还不如为ExecuteReader()创建包装器: />通过创建扩展SqlCommand或SqlConnection的类,以上两种想法都可以以更多的OO样式实现。

或者更好的是(第3部分),您可以为Connection StateChange创建一个事件处理程序,并在连接从Connection.Open()更改为_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;
}


只需将以下内容添加到创建OpenAndSetArithAbort(_Connection);实例的每个位置即可:

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();
        }
    }
}


无需更改现有代码。我刚刚在一个小型控制台应用程序中尝试了此方法,通过打印Closed的结果进行测试。它返回Open,但是如果我禁用事件处理程序,它将返回SqlConnection


为了完整起见,以下是一些无效的操作(无论是完全无效还是无效):



登录触发器:触发器,即使在计算机中运行相同的会话,即使在显式启动的事务中运行,也仍然是子进程,因此其设置(SELECT SESSIONPROPERTY('ARITHABORT');命令,本地临时表等)对于它来说是本地的,并且不能在该子进程结束时幸免。 br />在每个存储过程的开头添加1


这需要对现有项目进行大量工作,尤其是随着存储过程数量的增加
这不会帮助临时查询




评论


我刚刚测试过创建一个同时关闭ARITHABORT和ANSI_WARNINGS的简单数据库,创建了一个零位表,以及一个从中读取的简单.net客户端。 .net SqlClient显示为在SQL事件探查器的登录名中将ARITHABORT设置为off并将ANSI_WARNINGS设置为on,并且查询失败,除以预期的除以零。这似乎表明,设置数据库级别标志的首选解决方案对于更改.net SqlClient的默认设置无效。

–迈克
19年7月23日在1:18



可以确认设置服务器范围的user_options确实可行。

–迈克
19年7月23日在1:47

我还通过SELECT DATABASEPROPERTYEX('{database_name}','IsArithmeticAbortEnabled');观察到这一点。返回1,sys.dm_exec_sessions显示arithabort关闭,尽管我在Profiler中没有看到任何显式的SET。为什么会这样呢?

– andrew.rockwell
19年8月27日在19:50



SET ARITHABORT ON在c#中无效,但在SP(2008 R2)中有效。

– codemirror
20 Mar 20 '20 at 20:42



至于第3部分,我认为事件处理程序不会在后续查询之前为您提供任何执行保证,对吗?我要用它...

–冲击波
20年6月4日在8:24



#2 楼

选项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


我相信,最好的真正解决方案是简单地编辑代码,因为这样做是错误的,而其他任何修复方法也只能是一种解决方法。

评论


当.net连接在设置ArithAbort为off的情况下启动时,我认为这对为SQL Server启用它没有帮助。我希望可以在.net / C#方面完成某些工作。我提供了赏金,因为我看过建议。

–亨里克·斯塔恩·波尔森(Henrik Staun Poulsen)
2015年3月10日在6:51



Sankar涵盖了.net / C#方面,因此,这些几乎是唯一的选择。

– LowDBA-约翰·麦考尔(John McCall)
15年3月10日在13:08

我尝试了选项1,但没有效果。新的会话仍然显示为arithabort =0。我没有任何问题,只是试图解决潜在的问题。

–马克·弗里曼
17年3月31日在20:17

#3 楼

我不是这里的专家,但是您可以尝试以下类似方法。

String 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/zh-CN / transactsql / thread / d9e3e8ba-4948-4419-bb6b-dd5208bd7547 /

评论


是的,这就是我手动执行它的意思。事实是,与我一起使用的代码库在涉及数据库访问层时已经累积了很多技术债务,因此我必须重构数百种方法才能做到这一点。

– Peter Taylor
2011年5月3日19:45

#4 楼

没有设置可以强制SqlClient始终将ARITHABORT设置为打开,因此您必须按照说明进行设置。

有趣的是,Microsoft文档提供了SET ARITHABORT:-


您应该始终在登录会话中将ARITHABORT设置为ON。将
ARITHABORT设置为OFF会对查询优化产生负面影响,从而导致
性能问题。还有一点,在诊断此设置的性能问题时,必须非常小心。不同的设置选项将导致针对同一查询的不同查询计划。您的.Net代码可能会遇到性能问题(SET ARITHABORT OFF),但是当您在SSMS中运行相同的TSQL查询(默认情况下为SET ARITHABORT ON)时,这可能很好。这是因为.Net查询计划将不会重复使用,并且会生成新计划。例如,这可以潜在地消除参数嗅探问题并提供更好的性能。

评论


@HenrikStaunPoulsen-除非您坚持使用2000(或2000兼容级别),否则不会有任何区别。更高版本中的ANSI_WARNINGS暗示它,并且索引视图之类的东西可以正常工作。

–马丁·史密斯
2015年3月14日在21:12



请注意,.Net并不是硬编码来关闭ARITHABORT。 SSMS默认设置为打开。 .Net只是连接并使用服务器/数据库默认值。您可以在MS Connect上找到有关用户抱怨SSMS的默认行为的问题。请注意ARITHABORT文档页面上的警告。

–培根片
17-10-20在13:14



#5 楼

如果可以节省一些时间,就我而言(Entity Framework Core 2.0.3,ASP.Net Core API,SQL Server 2008 R2):


EF Core 2.0上没有拦截器(我认为它们会在2.1上很快推出)
更改全局数据库设置或设置user_options对我来说都是可以接受的(它们确实可以正常工作-我已经测试过),但是我不能冒险影响其他应用。

EF Core的临时查询(顶部为SET ARITHABORT ON;)不起作用。

最后,对我有用的解决方案是:合并存储过程,称为SET选项的原始查询,在EXEC之前用分号分隔,例如:

 // C# EF Core
int result = _context.Database.ExecuteSqlCommand($@"
SET ARITHABORT ON;
EXEC MyUpdateTableStoredProc
             @Param1 = {value1}
");
 


评论


有趣。感谢您发布使用EF Core的这些细微差别。只是好奇:您在这里所做的工作本质上就是我在我的答案的“替代方法”部分的ELSE小节中提到的包装器选项吗?我只是想知道是因为您在我的答案中提到了其他建议,由于其他限制,这些建议要么不起作用,要么不可行,但没有提及包装器选项。

–所罗门·鲁兹基
18-2-28在20:43

@SolomonRutzky它等效于该选项,但有一点细微之处在于它仅限于执行存储过程。在我的情况下,如果我用SET OPTION(手动或通过包装器)为原始更新查询添加了前缀,则它将不起作用。如果我将SET OPTION放入存储过程中,它将无法正常工作。唯一的方法是先执行SET OPTION,然后再执行EXEC存储过程。我选择自定义特定调用的方式,而不是使用包装器。很快我们将更新到SQLServer 2016,我可以清理这个问题。感谢您的回答,如果它有助于丢弃特定情况。

–克里斯·阿梅林克斯(Chris Amelinckx)
18 Mar 2 '18 at 5:06

#6 楼

基于针对EF6的Solomon Rutzy答案:

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.CommonDbCommand代替SqlCommandDbConnection代替SqlConnection

SQL事件探查器跟踪确认,在事务中执行任何其他命令之前,连接打开时将发送SET ARITHABORT ON

评论


我认为事件处理程序不会在后续查询之前为您提供任何执行保证,对吗?我要用它...

–冲击波
20年6月4日在8:26



@Shockwaver,如果后续命令仍然打开连接,则SET ARITHABORT保持打开状态。如果连接已关闭然后重新打开,则该事件将在执行任何其他命令之前再次触发。希望我能理解您的问题。

– CapNCook
20年5月5日在14:12

我的意思有所不同。如果打开连接然后发送查询,我认为您没有任何保证,事件处理程序(其中包含ARITHABORT查询)将在其他查询之前执行。这取决于处理程序的调用方式,您没有深入研究EF代码,我可能是错的

–冲击波
20年6月10日在10:19