it-swarm-eu.dev

Nastavit SqlClient jako výchozí na ARITHABORT ON

Nejdříve první: Používám MS SQL Server 2008 s databází na úrovni kompatibility 80 a připojuji se k ní pomocí .Net's System.Data.SqlClient.SqlConnection.

Z důvodů výkonu jsem vytvořil indexované zobrazení. Výsledkem je, že aktualizace tabulek uvedených v pohledu musí být provedeny pomocí ARITHABORT ON. Profiler však ukazuje, že SqlClient se připojuje k ARITHABORT OFF, takže aktualizace těchto tabulek selhávají.

Existuje centrální nastavení konfigurace, aby SqlClient používal ARITHABORT ON? Nejlepší, co jsem dokázal najít, je manuální spuštění, že pokaždé, když je připojení otevřeno, ale aktualizace existující kódové základny, aby to bylo provedeno, by byl docela velký úkol, a tak se snažím najít lepší způsob.

32
Peter Taylor

Zdánlivě preferovaný přístup

Měl jsem dojem, že následující byly již testovány jinými, zejména na základě některých připomínek. Ale moje testování ukazuje, že tyto dvě metody skutečně fungují na úrovni DB, i když se připojujete přes .NET SqlClient. Ty byly testovány a ověřeny ostatními.

Celý server

Můžete nastavit živatelské možnosti nastavení konfigurace serveru tak, aby bylo cokoli, co je aktuálně bitové ORed s 64 (hodnota pro ARITHABORT). Pokud nepoužíváte bit-wise OR (|)), Ale místo toho provedete přímé přiřazení (=), Vymažete všechny další stávající možnosti již povoleno.

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

Úroveň databáze

To lze nastavit na databázi pomocí 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;

Alternativní přístupy

Nepříjemnou zprávou je, že jsem toto téma hodně prohledal, jen abych zjistil, že za ta léta mnoho dalších prohledávalo toto téma, a neexistuje způsob, jak chování nakonfigurovat. z SqlClient. Některá dokumentace MSDN naznačuje, že to lze provést pomocí ConnectionString, ale neexistují žádná klíčová slova, která by umožňovala změnu těchto nastavení. Jiný dokument naznačuje, že je možné jej změnit pomocí Client Network Configuration/Configuration Manager, ale to se také nezdá možné. Proto, a bohužel, budete muset provést SET ARITHABORT ON; Ručně. Zde je několik způsobů, jak zvážit:

[~ # ~] Pokud [~ # ~] používáte Entity Framework 6 (nebo novější), můžete zkusit buď:

  • Použijte Database.ExecuteSqlCommand : context.Database.ExecuteSqlCommand("SET ARITHABORT ON;");
    V ideálním případě by to bylo provedeno jednou, po otevření připojení DB, a ne u každého dotazu.

  • Vytvořte interceptor pomocí:

    To vám umožní upravit SQL před spuštěním, v takovém případě jej můžete jednoduše předponou: SET ARITHABORT ON;. Nevýhodou je, že to bude na každý dotaz, pokud neuložíte lokální proměnnou pro zachycení stavu, zda byla nebo nebyla provedena, a otestujte to pokaždé (což opravdu není tolik práce navíc, ale pomocí ExecuteSqlCommand je pravděpodobně snazší).

Kterýkoli z nich vám umožní zvládnout to na jednom místě beze změny existujícího kódu.

[~ # ~] else [~ # ~] , můžete vytvořit metodu wrapperu, která to provede, podobně jako:

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

  return CommandToExec.ExecuteReader();
}

a potom prostě změňte aktuální _Reader = _Command.ExecuteReader(); reference na _Reader = ExecuteReaderWithSetting(_Command);.

To také umožňuje, aby nastavení bylo zpracováno na jednom místě, zatímco vyžaduje pouze minimální a zjednodušující změny kódu, které lze většinou provést pomocí funkce Najít a nahradit.

Ještě lepší ( Else Část 2), protože se jedná o nastavení úrovně připojení, nemusí být provedeno za každé volání SqlCommand.Execute __ (). Takže namísto vytvoření wrapperu pro ExecuteReader() vytvořte wrapper pro 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;
}

A pak stačí nahradit existující _Connection.Open(); odkazy za OpenAndSetArithAbort(_Connection);.

Oba výše uvedené nápady lze implementovat ve stylu více OO=) vytvořením třídy, která rozšiřuje buď SqlCommand, nebo SqlConnection.

Nebo ještě lépe ( Else Část 3), můžete vytvořit obsluhu události pro změnu stavu připojení a nechat ji nastavit vlastnost při změně připojení z Closed na Open následovně:

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

S tímto místem stačí přidat na každé místo, kde vytvoříte instanci SqlConnection, následující:

_Connection.StateChange += new StateChangeEventHandler(OnStateChange);

Není třeba provádět žádné změny stávajícího kódu. Právě jsem vyzkoušel tuto metodu v malé konzolové aplikaci, testování otiskem výsledku SELECT SESSIONPROPERTY('ARITHABORT');. Vrací 1, Ale pokud zakázám obsluhu událostí, vrátí 0.


Pro úplnost uvádíme některé věci, které nefungují (vůbec nebo ne tak účinně):

  • Logon Triggers : Triggery, i když běží ve stejné relaci, a to i v případě, že běží v rámci explicitně spuštěné transakce, je stále dílčí proces, a proto jeho nastavení (příkazy SET, local dočasné tabulky atd.) jsou místní a nepřežijí konec tohoto podprocesu.
  • Přidání SET ARITHABORT ON; Na začátek každé uložené procedury:
    • to vyžaduje hodně práce pro stávající projekty, zejména se zvyšujícím se počtem uložených procedur
    • to nepomůže dotazy ad hoc
29
Solomon Rutzky

Možnost 1

Kromě Sankarovo řešení bude fungovat nastavení aritmetického přerušení na úrovni serveru pro všechna připojení:

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

Od SQL 2014 je doporučeno být zapnuto pro všechna připojení:

Při přihlášení byste měli vždy nastavit ARITHABORT na ON. Nastavení ARITHABORT na OFF může negativně ovlivnit optimalizaci dotazu, což vede k problémům s výkonem.

Zdá se tedy, že je to ideální řešení.

Možnost 2

Pokud možnost 1 není životaschopná a pro většinu vašich volání SQL použijete uložené procedury (které byste měli vidět - ložené procedury vs. vložené SQL ), jednoduše povolte volbu v každé příslušné uložené proceduře:

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

Věřím, že nejlepším skutečným řešením je jednoduše upravit váš kód, protože je špatný a jakákoli jiná oprava je pouze řešením.

6
LowlyDBA

Nejsem zde odborník, ale můžete zkusit něco jako níže.

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

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

4
Sankar Reddy

Neexistuje žádné nastavení, které nutí SqlClient k tomu, aby vždy zapnul ARITHABORT, musíte to nastavit tak, jak budete popisovat.

Zajímavé z dokumentace společnosti Microsoft pro SET ARITHABORT : -

Při přihlášení byste měli vždy nastavit ARITHABORT na ON. Nastavení ARITHABORT na OFF může negativně ovlivnit optimalizaci dotazu, což vede k problémům s výkonem.

A přesto je .Net připojení ve výchozím nastavení pevně zakódováno?

Jako další bod musíte být při diagnostice problémů s výkonem při tomto nastavení velmi opatrní. Různé možnosti nastavení povedou k různým plánům dotazů pro stejný dotaz. Váš .Net kód může zaznamenat problém s výkonem (SET ARITHABORT OFF) a přesto, když spustíte stejný dotaz TSQL v SSMS (standardně SET ARITHABORT ON), může to být v pořádku. Důvodem je, že plán dotazů .Net nebude znovu použit a vygenerován nový plán. To by mohlo například eliminovat problém s čicháním parametrů a poskytnout mnohem lepší výkon.

2
Andy Jones

Pokud to někomu ušetří čas, v mém případě (Entity Framework Core 2.0.3, ASP.Net Core API, SQL Server 2008 R2):

  1. Na EF Core 2.0 nejsou žádné interceptory (myslím, že budou brzy k dispozici 2.1)
  2. Ani změna globálního nastavení DB, ani nastavení user_options byl pro mě přijatelný (fungují - testoval jsem), ale nemohl jsem riskovat dopad na jiné aplikace.

Ad-hoc dotaz od EF Core s SET ARITHABORT ON; nahoře nefunguje.

Nakonec řešení, které pro mě fungovalo, bylo: Kombinace uložené procedury, nazvané jako surový dotaz, s volbou SET před EXEC oddělená středníkem, jako je toto:

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

Staví na Solomon Rutzy odpověď , pro 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)
        ...

To používá System.Data.Common 's DbCommand místo SqlCommand a DbConnection místo SqlConnection.

Sledování SQL Profiler potvrzuje, SET ARITHABORT ON se odešle při otevření spojení před provedením dalších příkazů v transakci.

0
CapNCook