it-swarm.com.de

Setzen Sie SqlClient standardmäßig auf ARITHABORT ON

Das Wichtigste zuerst: Ich verwende MS SQL Server 2008 mit einer Datenbank auf Kompatibilitätsstufe 80 und verbinde mich mit .Nets System.Data.SqlClient.SqlConnection.

Aus Leistungsgründen habe ich eine indizierte Ansicht erstellt. Daher müssen Aktualisierungen von Tabellen, auf die in der Ansicht verwiesen wird, mit ARITHABORT ON Durchgeführt werden. Der Profiler zeigt jedoch an, dass SqlClient eine Verbindung mit ARITHABORT OFF Herstellt, sodass Aktualisierungen dieser Tabellen fehlschlagen.

Gibt es eine zentrale Konfigurationseinstellung, mit der SqlClient ARITHABORT ON Verwendet? Das Beste, was ich finden konnte, ist, jedes Mal, wenn eine Verbindung geöffnet wird, manuell auszuführen. Das Aktualisieren der vorhandenen Codebasis, um dies zu tun, wäre jedoch eine ziemlich große Aufgabe, daher bin ich sehr daran interessiert, einen besseren Weg zu finden.

32
Peter Taylor

Scheinbar bevorzugter Ansatz

Ich hatte den Eindruck, dass das Folgende bereits von anderen getestet wurde, insbesondere aufgrund einiger Kommentare. Meine Tests zeigen jedoch, dass diese beiden Methoden tatsächlich auf DB-Ebene funktionieren, selbst wenn eine Verbindung über .NET SqlClient hergestellt wird. Diese wurden von anderen getestet und verifiziert.

Serverweit

Sie können die Serverkonfigurationseinstellung Benutzeroptionen so einstellen, wie sie derzeit bitweise OR mit 64 (der Wert für ARITHABORT) ist. Wenn Sie nicht das bitweise OR (|) Verwenden, sondern stattdessen eine direkte Zuweisung (=) Ausführen, werden alle anderen vorhandenen Optionen gelöscht bereits aktiviert.

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

Datenbankebene

Dies kann pro Datenbank über ALTER DATABASE SET eingestellt werden:

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;

Alternative Ansätze

Die nicht so gute Nachricht ist, dass ich viel nach diesem Thema gesucht habe, nur um festzustellen, dass im Laufe der Jahre viele andere viel nach diesem Thema gesucht haben und es keine Möglichkeit gibt, das Verhalten zu konfigurieren von SqlClient. Einige MSDN-Dokumentationen implizieren, dass dies über einen ConnectionString erfolgen kann, es gibt jedoch keine Schlüsselwörter, mit denen diese Einstellungen geändert werden könnten. Ein anderes Dokument impliziert, dass es über Client Network Configuration/Configuration Manager geändert werden kann, aber das scheint auch nicht möglich zu sein. Daher müssen Sie SET ARITHABORT ON; Leider manuell ausführen. Hier sind einige Möglichkeiten zu berücksichtigen:

[~ # ~] Wenn Sie [~ # ~] Entity Framework 6 (oder neuer) verwenden, können Sie Folgendes versuchen:

  • Verwenden Sie Database.ExecuteSqlCommand : context.Database.ExecuteSqlCommand("SET ARITHABORT ON;");
    Idealerweise wird dies einmal nach dem Öffnen der DB-Verbindung und nicht für jede Abfrage ausgeführt.

  • Erstellen Sie ein Interceptor über entweder:

    Auf diese Weise können Sie die SQL ändern, bevor sie ausgeführt wird. In diesem Fall können Sie ihr einfach Folgendes voranstellen: SET ARITHABORT ON;. Der Nachteil hierbei ist, dass dies für jede Abfrage gilt, es sei denn, Sie speichern eine lokale Variable, um den Status zu erfassen, ob sie ausgeführt wurde oder nicht, und testen Sie dies jedes Mal (was wirklich nicht so viel zusätzliche Arbeit ist, aber die Verwendung von ExecuteSqlCommand ist wahrscheinlich einfacher).

In beiden Fällen können Sie dies an einer Stelle erledigen, ohne vorhandenen Code zu ändern.

[~ # ~] sonst [~ # ~] können Sie eine Wrapper-Methode erstellen, die dies tut, ähnlich wie:

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

  return CommandToExec.ExecuteReader();
}

und ändern Sie dann einfach die aktuellen _Reader = _Command.ExecuteReader(); Referenzen in _Reader = ExecuteReaderWithSetting(_Command);.

Auf diese Weise kann die Einstellung auch an einem einzigen Ort vorgenommen werden, während nur minimale und vereinfachte Codeänderungen erforderlich sind, die größtenteils über Suchen und Ersetzen vorgenommen werden können.

Besser noch ( Sonst Teil 2), da dies eine Einstellung für die Verbindungsebene ist, muss dies nicht sein wird bei jedem Aufruf von SqlCommand.Execute __ () ausgeführt. Anstatt einen Wrapper für ExecuteReader() zu erstellen, erstellen Sie einen Wrapper für 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;
}

Und dann ersetzen Sie einfach die vorhandenen _Connection.Open(); Referenzen durch OpenAndSetArithAbort(_Connection);.

Beide oben genannten Ideen können im Stil more OO) implementiert werden, indem eine Klasse erstellt wird, die entweder SqlCommand oder SqlConnection erweitert.

Oder noch besser ( Sonst Teil 3), Sie können einen Ereignishandler für den Connection StateChange erstellen und festlegen Die Eigenschaft, wenn sich die Verbindung von Closed zu Open wie folgt ändert:

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

Wenn dies vorhanden ist, müssen Sie nur an jeder Stelle, an der Sie eine SqlConnection -Instanz erstellen, Folgendes hinzufügen:

_Connection.StateChange += new StateChangeEventHandler(OnStateChange);

Es sind keine Änderungen am vorhandenen Code erforderlich. Ich habe diese Methode gerade in einer kleinen Konsolen-App ausprobiert und durch Drucken des Ergebnisses von SELECT SESSIONPROPERTY('ARITHABORT'); getestet. Es gibt 1 Zurück, aber wenn ich den Ereignishandler deaktiviere, wird 0 Zurückgegeben.


Der Vollständigkeit halber sind hier einige Dinge aufgeführt, die nicht funktionieren (entweder überhaupt nicht oder nicht so effektiv):

  • Logon Triggers : Trigger sind auch während der Ausführung in derselben Sitzung und selbst wenn sie innerhalb einer explizit gestarteten Transaktion ausgeführt werden, immer noch ein Unterprozess und daher seine Einstellungen (SET -Befehle, lokal temporäre Tabellen usw.) sind lokal und überleben das Ende dieses Unterprozesses nicht.
  • Hinzufügen von SET ARITHABORT ON; Am Anfang jeder gespeicherten Prozedur:
    • dies erfordert viel Arbeit für bestehende Projekte, insbesondere wenn die Anzahl der gespeicherten Prozeduren zunimmt
    • dies hilft nicht bei Ad-hoc-Abfragen
29
Solomon Rutzky

Option 1

Abgesehen von Sankars Lösung funktioniert das Festlegen der Einstellung für den arithmetischen Abbruch auf Serverebene für alle Verbindungen:

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

Ab SQL 2014 ist es empfohlen eingeschaltet zu sein für alle Verbindungen:

Sie sollten ARITHABORT in Ihren Anmeldesitzungen immer auf ON setzen. Das Setzen von ARITHABORT auf OFF kann sich negativ auf die Abfrageoptimierung auswirken und zu Leistungsproblemen führen.

Dies scheint also die ideale Lösung zu sein.

Option 2

Wenn Option 1 nicht realisierbar ist und Sie für die meisten Ihrer SQL-Aufrufe gespeicherte Prozeduren verwenden (siehe Gespeicherte Prozeduren vs. Inline-SQL ), aktivieren Sie einfach die Option in jeder relevanten gespeicherten Prozedur:

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

Ich glaube, die beste echte Lösung besteht darin, einfach Ihren Code zu bearbeiten, da er falsch ist und jede andere Korrektur lediglich eine Problemumgehung darstellt.

6
LowlyDBA

Ich bin hier kein Experte, aber Sie können so etwas wie unten versuchen.

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

Ref: http://social.msdn.Microsoft.com/Forums/en-US/transactsql/thread/d9e3e8ba-4948-4419-bb6b-dd5208bd7547/

4
Sankar Reddy

Es gibt keine Einstellung, mit der SqlClient gezwungen wird, ARITHABORT immer einzuschalten. Sie müssen dies wie beschrieben festlegen.

Interessanterweise aus dem Microsoft-Dokumentation für SET ARITHABORT : -

Sie sollten ARITHABORT in Ihren Anmeldesitzungen immer auf ON setzen. Das Setzen von ARITHABORT auf OFF kann sich negativ auf die Abfrageoptimierung auswirken und zu Leistungsproblemen führen.

Und doch ist die .NET-Verbindung fest codiert, um dies standardmäßig auszulösen?

Als weiterer Punkt müssen Sie bei der Diagnose von Leistungsproblemen mit dieser Einstellung sehr vorsichtig sein. Unterschiedliche Set-Optionen führen zu unterschiedlichen Abfrageplänen für dieselbe Abfrage. Bei Ihrem .Net-Code kann ein Leistungsproblem auftreten (SET ARITHABORT OFF). Wenn Sie jedoch dieselbe TSQL-Abfrage in SSMS ausführen (standardmäßig SET ARITHABORT ON), kann dies in Ordnung sein. Dies liegt daran, dass der .NET-Abfrageplan nicht wiederverwendet und ein neuer Plan generiert wird. Dies könnte beispielsweise ein Parameter-Sniffing-Problem möglicherweise beseitigen und eine viel bessere Leistung erzielen.

2
Andy Jones

Wenn es jemandem Zeit spart, in meinem Fall (Entity Framework Core 2.0.3, ASP.Net Core API, SQL Server 2008 R2):

  1. Es gibt keine Interceptors auf EF Core 2.0 (ich denke, sie werden bald auf 2.1 verfügbar sein)
  2. Weder die globale DB-Einstellung ändern noch die user_options war für mich akzeptabel (sie funktionieren - ich habe getestet), aber ich konnte nicht riskieren, andere Anwendungen zu beeinflussen.

Eine Ad-hoc-Abfrage von EF Core mit SET ARITHABORT ON; oben, funktioniert NICHT.

Schließlich war die Lösung, die für mich funktioniert hat: Kombinieren einer gespeicherten Prozedur, die als Rohabfrage aufgerufen wird, mit der Option SET vor EXEC, die durch ein Semikolon getrennt ist, wie folgt:

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

Aufbauend auf Antwort von Solomon Rutzy , für 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)
        ...

Dies verwendet System.Data.Common 's DbCommand anstelle von SqlCommand und DbConnection anstelle von SqlConnection.

Ein SQL Profiler-Trace bestätigt, SET ARITHABORT ON wird beim Öffnen der Verbindung gesendet, bevor andere Befehle in der Transaktion ausgeführt werden.

0
CapNCook