web-dev-qa-db-ja.com

SqlClientのデフォルトをARITHABORT ONにする

まず最初に、互換性レベル80のデータベースでMS SQL Server 2008を使用し、.NetのSystem.Data.SqlClient.SqlConnectionを使用してそれに接続しています。

パフォーマンス上の理由から、インデックス付きビューを作成しました。その結果、ビューで参照されるテーブルへの更新はARITHABORT ONで行う必要があります。ただし、プロファイラーはSqlClientがARITHABORT OFFで接続していることを示しているため、これらのテーブルへの更新は失敗しています。

SqlClientでARITHABORT ONを使用するための中央構成設定はありますか?私が見つけた最高の方法は、接続が開かれるたびに手動で実行することですが、これを行うために既存のコードベースを更新するのはかなり大きなタスクになるため、より良い方法を見つけたいと思っています。

32
Peter Taylor

一見好ましいアプローチ

特に、いくつかのコメントに基づいて、以下はすでに他の人によってテストされているという印象を受けました。しかし、私のテストでは、.NET SqlClient経由で接続している場合でも、これら2つの方法が実際にDBレベルで機能することが示されています。 これらは他のユーザーによってテストおよび検証されています。

サーバー全体

ユーザーオプション サーバー構成設定を、現在ビット単位のORedである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;_を実行する必要があります。考慮すべきいくつかの方法を次に示します。

[〜#〜] if [〜#〜]Entity Framework 6(またはそれ以降)を使用している場合は、次のいずれかを試すことができます。

  • Database.ExecuteSqlCommand を使用します:context.Database.ExecuteSqlCommand("SET ARITHABORT ON;");
    理想的には、これはDBの接続を開いた後に1回実行され、クエリごとではありません。

  • インターセプター を作成します。

    これにより、SQLを実行する前に変更できます。その場合は、単にプレフィックスとして_SET ARITHABORT ON;_を付けることができます。ここでの欠点は、ローカル変数を保存して実行されたかどうかの状態をキャプチャして毎回テストする場合を除いて、クエリごとになることです(実際にはそれほど余分な作業ではありませんが、ExecuteSqlCommandはおそらく簡単です)。

これらのどちらでも、既存のコードを変更することなく、1つの場所でこれを処理できます。

[〜#〜] else [〜#〜]、これを行うラッパーメソッドを作成すると、次のようになります。

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

  return CommandToExec.ExecuteReader();
}
_

次に、現在の_Reader = _Command.ExecuteReader();参照を_Reader = ExecuteReaderWithSetting(_Command);に変更します。

これを行うと、設定を単一の場所で処理できるようになりますが、ほとんどの場合、検索と置換を介して実行できる最小限の単純なコード変更のみが必要です。

さらに良いElseパート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スタイルで実装できます。

またはより良いElseパート3)、接続状態変更のイベントハンドラーを作成し、次のように、接続が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_が返されます。


完全を期すために、(まったく機能しないか、それほど効果的ではない)機能しないものを以下に示します。

  • Logon Triggers :同じセッションで実行中であっても、明示的に開始されたトランザクション内で実行されている場合でも、トリガーはサブプロセスであり、したがってその設定(SETコマンド、ローカル一時テーブルなど)はローカルであり、そのサブプロセスの終了後も存続しません。
  • 各ストアドプロシージャの先頭に_SET ARITHABORT ON;_を追加します。
    • 特にストアドプロシージャの数が増えると、既存のプロジェクトで多くの作業が必要になります。
    • これはアドホッククエリには役立ちません
29
Solomon Rutzky

オプション1

Sankar's solution を除いて、すべての接続に対してサーバーレベルで算術アボート設定を設定すると機能します。

EXEC sys.sp_configure N'user options', N'64'
GO
RECONFIGURE WITH OVERRIDE
GO

SQL 2014以降は、すべての接続で オンにすることをお勧めします です。

ログオンセッションでは、常にARITHABORTをONに設定する必要があります。 ARITHABORTをOFFに設定すると、クエリの最適化に悪影響を及ぼし、パフォーマンスの問題を引き起こす可能性があります。

したがって、これは理想的なソリューションのようです。

オプション2

オプション1が実行可能ではなく、ほとんどのSQL呼び出しにストアード・プロシージャーを使用している場合(そうする必要があります ストアード・プロシージャーvs.インラインSQL を参照)、関連する各ストアード・プロシージャーでオプションを有効にするだけです。

CREATE PROCEDURE ...
AS 
BEGIN
   SET ARITHABORT ON
   SELECT ...
END
GO

私はここでの最良のrealソリューションはコードを単に編集することだと信じています。それは間違っており、他の修正は単なる回避策です。

6
LowlyDBA

私はここの専門家ではありませんが、以下のようなことを試すことができます。

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/en-US/transactsql/thread/d9e3e8ba-4948-4419-bb6b-dd5208bd7547/

4
Sankar Reddy

SqlClientにARITHABORTを常にオンに設定させる設定はありません。説明のとおり、これを設定する必要があります。

おもしろいことに SET ARITHABORTに関するMicrosoftのドキュメント :-

ログオンセッションでは、常にARITHABORTをONに設定する必要があります。 ARITHABORTをOFFに設定すると、クエリの最適化に悪影響を及ぼし、パフォーマンスの問題を引き起こす可能性があります。

そして、.Net接続はこれをデフォルトでオフに設定するようにハードコードされていますか?

別の点として、この設定でパフォーマンスの問題を診断するときは、非常に注意する必要があります。設定オプションが異なると、同じクエリのクエリプランも異なります。 .Netコードでパフォーマンスの問題(SET ARITHABORT OFF)が発生する可能性がありますが、SSMSで同じTSQLクエリを実行すると(デフォルトではSET ARITHABORT ON)、問題ない可能性があります。これは、.Netクエリプランが再利用されず、新しいプランが生成されるためです。これにより、たとえば、パラメータスニッフィングの問題が排除され、パフォーマンスが大幅に向上する可能性があります。

2
Andy Jones

私の場合、誰かを時間を節約する場合(Entity Framework Core 2.0.3、ASP.Net Core API、SQL Server 2008 R2):

  1. EF Core 2.0にはインターセプターはありません(2.1ではまもなく利用可能になると思います)
  2. グローバルDB設定を変更することも、user_optionsを設定することも受け入れられませんでした(機能します-私はテストしました)、他のアプリケーションに影響を与えるリスクはありませんでした。

先頭にSET ARITHABORT ON;を付けたEF Coreからのアドホッククエリは機能しません。

最後に、私にとってうまくいった解決策は、次のように、セミコロンで区切られたSETの前にEXECオプションを付けて、生のクエリとして呼び出されるストアドプロシージャを結合することです。

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

EF6の Solomon Rutzy answer に基づいて構築:

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プロファイラートレースが確認、SET ARITHABORT ONは、接続が開いたときに、トランザクションで他のコマンドが実行される前に送信されます。

0
CapNCook