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


32

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

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

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

回答:


28

一見好ましいアプローチ

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

サーバー全体

あなたは、設定できるユーザー・オプション・それが現在ビット単位であるものは何でもする、サーバーの構成設定をOR64とED(の値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);

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

さらに良いことに他のパート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のいずれかを拡張するクラスを作成することにより、より多くのオブジェクト指向スタイルで実装できます。

それともいっそのことそれ以外のパート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のデフォルトを変更する場合、dbレベルフラグを設定する推奨ソリューションが機能しないことを示しているようです。
マイク

ただし、サーバー全体のuser_optionsの設定が機能することを確認できます。
マイク

またSELECT DATABASEPROPERTYEX('{database_name}', 'IsArithmeticAbortEnabled');、1 を返すことで、sys.dm_exec_sessionsがarithabortをオフにすることを確認していますが、プロファイラーには明示的な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#側でできることを望んでいました。私は推薦を見ていたので、私は賞金を上げました。
ヘンリックスタンポールセン

1
.net / C#側がSankarの対象であるため、これらはほとんど唯一のオプションです。
LowlyDBA

オプション1を試しましたが、効果はありませんでした。新しいセッションは、まだarithabort = 0として表示されます。問題は発生していませんが、潜在的な問題を先取りしようとしています。
マークフリーマン

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


はい、それは私が手動で実行するということです。問題は、私が作業しているコードベースは、DBアクセスレイヤーに関してかなりの技術的負債を生じているため、この方法で行うには数百のメソッドをリファクタリングする必要があります。
ピーターテイラー

2

SqlClientでARITHABORTを常にオンに設定する設定はありません。説明どおりに設定する必要があります。

興味深いことに、SET ARITHABORTのMicrosoftのドキュメントから:-

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

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

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


1
@HenrikStaunPoulsen-2000(または2000互換性レベル)を使用して動けない限り、違いはありません。それANSI_WARNINGS以降のバージョンではon によって暗示されており、インデックス付きビューなどが正常に機能します。
マーティンスミス

.NetはARITHABORTをオフに設定するためにハードコードされていないことに注意してください。 SSMSはデフォルトでに設定さます。.Netは、サーバー/データベースのデフォルトを接続して使用しているだけです。SSMSのデフォルトの動作について不平を言うユーザーに関するMS Connectの問題を見つけることができます。ARITHABORT docページの警告に注意してください
ベーコンビット

2

私の場合(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受け入れられませんでした(動作します-テストしました)が、他のアプリケーションに影響を与えるリスクはありませんでした。

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サブセクションで言及したラッパーオプションですか?あなたは私の答えで他の提案が機能していないか、他の制約のために実行可能ではないことを言及したが、ラッパーオプションについては言及しなかったので、私はただ疑問に思っていました。
ソロモンラッツキー

@SolomonRutzkyこれは、ストアドプロシージャの実行に制限されているというニュアンスがあるこのオプションと同等です。私の場合、生の更新クエリの前にSET OPTION(手動またはラッパー経由)を付けた場合、機能しません。ストアドプロシージャ内にSET OPTIONを配置すると、機能しません。唯一の方法は、SET OPTIONを実行してから、同じバッチでEXECストアドプロシージャを実行することでした。これは、ラッパーを作成する代わりに、特定の呼び出しをカスタマイズすることを選択した方法です。すぐに、SQLServer 2016に更新し、これをクリーンアップできます。特定のシナリオを破棄するのに役立ったのであれば、回答ありがとうございます。
クリスアメリンクス

0

上で構築ソロモン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.CommonDbCommand代わりにSqlCommandとをDbConnection使用しSqlConnectionます。

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.