将SqlClient默认设置为ARITHABORT ON


32

首先,第一件事:我将MS SQL Server 2008与兼容级别为80的数据库一起使用,并使用.Net的数据库进行连接System.Data.SqlClient.SqlConnection

出于性能原因,我创建了索引视图。结果,需要使用对视图中引用的表进行更新ARITHABORT ON。但是,探查器显示SqlClient正在与进行连接ARITHABORT OFF,因此对这些表的更新失败。

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

Answers:


28

看似首选的方法

我的印象是,以下人员已经过测试,尤其是根据某些评论。但是我的测试表明,即使通过.NET连接,这两种方法的确确实可以在数据库级别工作SqlClient这些已经过其他人的测试和验证。

服务器范围

您可以将用户选项服务器配置设置设置为当前按位设置OR的值(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为每个数据库设置

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.ExecuteSqlCommandcontext.Database.ExecuteSqlCommand("SET ARITHABORT ON;");
    理想情况下,这将在打开数据库连接后执行一次,而不是针对每个查询执行一次。

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

    这将允许您在执行SQL之前对其进行修改,在这种情况下,您可以简单地为它加上前缀:SET ARITHABORT ON;。这里的缺点是,它会按每个查询,除非你存储一个局部变量来捕捉它是否已被执行,每次的状态和测试(这真的不是那么多额外的工作,但使用ExecuteSqlCommand是可能更容易)。

这些中的任何一个都可以让您一站式处理,而无需更改任何现有代码。

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部分),您可以创建连接StateChange事件处理程序,并将它设置属性时,从连接更改ClosedOpen如下:

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;到每个存储过程的开头:
    • 对于现有项目,这需要大量工作,尤其是随着存储过程数量的增加
    • 这对临时查询没有帮助

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

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

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

6

选项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连接以开头时,我认为这对为SQL Server启用它没有帮助set ArithAbort off。我希望可以在.net / C#方面完成某些工作。我提供了赏金,因为我看过建议。
Henrik Staun Poulsen

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

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

4

我不是这里的专家,但是您可以尝试以下操作。

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/


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

2

没有强制SqlClient始终将ARITHABORT设置为on的设置,您必须按照说明进行设置。

有趣的是,来自Microsoft文档SET ARITHABORT:-

您应该始终在登录会话中将ARITHABORT设置为ON。将ARITHABORT设置为OFF会对查询优化产生负面影响,从而导致性能问题。

但是.Net连接是否经过硬编码默认将其设置为关闭?

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


1
@HenrikStaunPoulsen-除非您坚持使用2000(或2000兼容级别),否则不会有任何区别。ANSI_WARNINGS在更高版本中暗示了这一点,并且诸如索引视图之类的东西可以正常工作。
马丁·史密斯

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

2

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

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

SET ARITHABORT ON;顶部带有EF Core的临时查询不起作用。

最后,对我有用的解决方案是:将存储过程(称为原始查询)与SET选项组合起来,然后EXEC用分号分隔,如下所示:

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

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

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

0

基于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替代SqlCommand,以及DbConnection替代SqlConnection

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.