SqlClient'i ARITHABORT ON konumunda varsayılan yapın


32

Öncelikle ilk şeyler: MS SQL Server 2008'i 80 uyumluluk düzeyinde bir veritabanıyla kullanıyorum ve buna System.Data.SqlClient.SqlConnection.

Performans nedeniyle indekslenmiş bir görünüm oluşturdum. Sonuç olarak, görünümde başvurulan tablolarda güncellemelerin yapılması gerekir ARITHABORT ON. Ancak, profiler, SqlClient'in bağlandığını gösterir ARITHABORT OFF, bu nedenle bu tablolarda güncellemeler başarısız olur.

SqlClient'i kullanmak için merkezi bir konfigürasyon ayarı var mı ARITHABORT ON? Bulduğum en iyi şey, bir bağlantı her açıldığında manuel olarak gerçekleştirmektir, ancak bunu yapmak için mevcut kod tabanını güncellemek oldukça büyük bir görevdir, bu yüzden daha iyi bir yol bulmak istiyorum.

Yanıtlar:


28

Görünüşe göre tercih edilen yaklaşım

Aşağıdakilerin, özellikle yorumların bazılarına dayanarak başkaları tarafından test edildiği izlenimi altındaydım. Ancak testlerim, bu iki yöntemin aslında .NET üzerinden bağlanırken bile DB düzeyinde çalıştığını gösteriyor SqlClient. Bunlar başkaları tarafından test edilmiş ve doğrulanmıştır.

Sunucu çapında

Ayarlayabilirsiniz kullanıcı seçenekleri o bit çapında şu anda ne olursa olsun olmak sunucu yapılandırma ayarını OR64 (değeri ile ed ARITHABORT). Bit bilge VEYA ( |) kullanmazsanız bunun yerine düz bir atama ( =) yaparsanız, zaten etkin olan diğer mevcut seçenekleri silersiniz.

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

Veritabanı düzeyinde

Bu ALTER DATABASE SET ile veri tabanı başına ayarlanabilir :

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;

Alternatif yaklaşımlar

Çok iyi olmayan bir haber, bu konuyla ilgili çok fazla araştırma yaptığım, yalnızca yıllar içinde birçok kişinin bu konuda çok fazla araştırma yaptıklarını ve bu davranışı yapılandırmanın mümkün olmadığını bulmak. arasında SqlClient. Bazı MSDN belgeleri, bir ConnectionString yoluyla yapılabileceğini belirtir, ancak bu ayarların değiştirilmesine izin verecek hiçbir Anahtar Kelime yoktur. Başka bir belge, İstemci Ağ Yapılandırması / Yapılandırma Yöneticisi aracılığıyla değiştirilebileceğini ancak bunun da mümkün görünmediğini belirtir. Dolayısıyla, ne yazık ki, SET ARITHABORT ON;elle çalıştırmanız gerekecektir . İşte göz önünde bulundurmanız gereken bazı yollar:

EĞER sen Varlık Framework 6 (veya daha yeni) kullanıyorsanız, ya deneyebilirsiniz:

  • Database.ExecuteSqlCommandcontext.Database.ExecuteSqlCommand("SET ARITHABORT ON;");
    komutunu kullanın : İdeal olarak, DB bağlantısı açıldıktan sonra her sorgu için değil, bir kez çalıştırılabilir.

  • Aşağıdakilerden birini kullanarak bir önleyici oluşturun :

    Bu basitçe, öneki olabilir ki bu durumda buna çalıştırılmadan önce SQL, değiştirmelerine olanak tanır: SET ARITHABORT ON;. Buradaki dezavantajı, her seferinde gerçekleştirilip gerçekleştirilmediğinin durumunu tespit etmek için yerel bir değişken kaydetmediğiniz sürece her sorgu başına geleceğidir (her seferinde bunun için sınamak (bu gerçekten fazladan bir iş değildir, ancak kullanmaktır ExecuteSqlCommand). muhtemelen daha kolay).

Bunlardan biri, varolan herhangi bir kodu değiştirmeden, bunu bir noktada ele almanıza izin verecektir.

ELSE , şuna benzer , bunu yapan bir sarmalayıcı yöntemi oluşturabilirsiniz:

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

  return CommandToExec.ExecuteReader();
}

ve sonra sadece mevcut _Reader = _Command.ExecuteReader();referansları değiştirin _Reader = ExecuteReaderWithSetting(_Command);.

Bunu yapmak aynı zamanda ayarın tek bir konumda yapılmasına izin verirken, çoğunlukla Bul ve Değiştir ile yapılabilecek yalnızca minimal ve basit kod değişikliklerini gerektirir.

Daha da iyisi ( Başka Bölüm 2), bu bir bağlantı seviyesi ayarı olduğundan, her SqlCommand.Execute __ () çağrısı başına yürütülmesi gerekmez. Dolayısıyla ExecuteReader(), için bir sarmalayıcı oluşturmak yerine , için bir sarmalayıcı oluşturun 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;
}

Ve sonra sadece var olan _Connection.Open();referansları değiştirin OpenAndSetArithAbort(_Connection);.

Yukarıdaki fikirlerin her ikisi de, SqlCommand veya SqlConnection'ı genişleten bir Sınıf oluşturarak daha fazla OO tarzında uygulanabilir.

Veya Daha iyisi ( Else Bölüm 3), sen Connection StateChange için bir olay işleyicisi oluşturmak ve özelliğini ayarlayın sahip olabilecekken gelen bağlantı değişiklikleri Closediçin Openaşağıdaki gibi:

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

Bu yerinde, yalnızca bir SqlConnectionörnek oluşturduğunuz her yere aşağıdakileri eklemeniz gerekir :

_Connection.StateChange += new StateChangeEventHandler(OnStateChange);

Mevcut kodda değişiklik yapılması gerekmez. Küçük bir konsol uygulamasında bu yöntemi denedim, sonucunu yazdırarak test ettim SELECT SESSIONPROPERTY('ARITHABORT');. Dönüyor 1, ancak Olay İşleyiciyi devre dışı bırakırsam döndürür 0.


Tamlık uğruna, işte işe yaramayan bazı şeyler var (ya hiç ya da etkili biçimde değil):

  • Oturum Açma Tetikleyicileri : Aynı oturumda çalışırken ve açıkça başlatılmış bir işlem içinde çalıştırılsa bile, tetikleyiciler hala bir alt işlemdir ve bu nedenle ayarları ( SETkomutlar, yerel geçici tablolar, vb.) Yereldir ve hayatta kalmazlar. bu alt sürecin sonu.
  • SET ARITHABORT ON;Her saklı yordamın başlangıcına ekleme :
    • Bu, mevcut projeler için, özellikle saklı işlemlerin sayısı arttıkça, çok fazla çalışma gerektirir.
    • bu geçici sorgulara yardımcı olmaz

Hem ARITHABORT hem de ANSI_WARNINGS ile basit bir veritabanı oluşturmayı test ettim, içinde sıfır olan bir tablo ve basit bir .net istemcisi oluşturdum. .Net SqlClient, sql profiler oturum açma işleminde ARITHABORT'u kapalı ve ANSI_WARNINGS ayarının açık olduğunu ve beklendiği gibi sıfıra bölme işleminde sorgunun başarısız olduğunu gösterdi. Bu, db seviye bayraklarının ayarlanması için tercih edilen çözümün, .net SqlClient için varsayılanı değiştirme konusunda çalışmayacağını göstermektedir.
Mike,

Ancak sunucu genelinde user_options ayarının işe yaradığını onaylayabilir.
Mike,

Ayrıca SELECT DATABASEPROPERTYEX('{database_name}', 'IsArithmeticAbortEnabled');, 1 döndürerek, sys.dm_exec_sessions'ın arithabort kapalı olduğunu gösterir, ancak Profiler'de açık bir SETS göremiyorum. Bu neden olsun ki?
andrew.rockwell

6

seçenek 1

Sankar'ın çözümünün yanı sıra, tüm bağlantılar için sunucu düzeyinde aritmetik iptal ayarının yapılması işe yarayacaktır:

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

SQL 2014 itibariyle tüm bağlantılarda açık olması tavsiye edilir :

Oturum açma oturumlarınızda ARITHABORT'u her zaman ON (AÇIK) olarak ayarlamalısınız. ARITHABORT'un OFF olarak ayarlanması, performans sorunlarına yol açan sorgu optimizasyonunu olumsuz yönde etkileyebilir.

Yani bu ideal bir çözüm gibi görünüyor.

seçenek 2

Seçenek 1 uygun değilse ve SQL çağrılarınızın çoğu için saklı yordamlar kullanıyorsanız (yapmanız gerekenler, Saklı Prosedürler ve Satır İçi SQL'e bakın ) o zaman ilgili her saklı yordamda seçeneği etkinleştirin:

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

Ben en iyi inanıyoruz gerçek bunun yanlış ve diğer düzeltme yalnızca geçici bir çözüm olduğu gibi burada çözüm, basitçe kodunuzu düzenlemektir.


.Net bağlantısı ile başladığında, SQL Server için ayar yardımcı olur sanmıyorum set ArithAbort off. Net / C # tarafında yapılabilecek bir şey umuyordum. Ödül verdim, çünkü tavsiyeyi gördüm.
Henrik Staun Poulsen 10:15

1
.Net / C # tarafı Sankar'ın kapsadığı şeydir, bu yüzden bunlar hemen hemen tek seçenek.
LowlyDBA

Seçenek 1'i denedim ve hiçbir etkisi olmadı. Yeni oturumlar hala arithabort = 0 olarak gösteriliyor. Ondan herhangi bir sorunum yok, sadece potansiyel sorunların üstesinden gelmeye çalışıyorum.
Mark Freeman,

4

Ben burada uzman değilim ama aşağıdaki gibi bir şeyi deneyebilirsiniz.

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/


Evet, el ile uygulayarak kastediyorum. Mesele şu ki, birlikte çalıştığım kod temeli, DB erişim katmanına gelince, bir miktar teknik borç tahakkuk ettirdi, bu yüzden bunu yapmak için birkaç yüz yöntemi yeniden gözden geçirmem gerekecek.
Peter Taylor

2

SqlClient'i her zaman ARITHABORT'u açmaya zorlayacak bir ayar yoktur, bunu tanımladığınız şekilde ayarlamanız gerekir.

SET ARITHABORT için Microsoft dokümantasyonundan ilginç şekilde : -

Oturum açma oturumlarınızda ARITHABORT'u her zaman ON (AÇIK) olarak ayarlamalısınız. ARITHABORT'un OFF olarak ayarlanması, performans sorunlarına yol açan sorgu optimizasyonunu olumsuz yönde etkileyebilir.

Yine de .Net bağlantısı bunu varsayılan olarak kapatmak için kodlanmış durumda mı?

Başka bir nokta olarak, bu ayara ilişkin performans sorunlarını teşhis ederken çok dikkatli olmalısınız. Farklı ayar seçenekleri, aynı sorgu için farklı sorgu planları ile sonuçlanacaktır. .Net kodunuz bir performans sorunu yaşayabilir (SET ARITHABORT OFF) ve yine de aynı TSQL sorgusunu SSMS'de çalıştırdığınızda (varsayılan olarak SET ARITHABORT ON). Bunun nedeni, .NET sorgu planının yeniden kullanılmayacak ve yeni bir plan oluşturulacak olmasıdır. Bu, örneğin bir parametre koklama problemini ortadan kaldırabilir ve daha iyi performans sağlayabilir.


1
@HenrikStaunPoulsen - 2000 (veya 2000 uyumluluk düzeyi) kullanarak takılmadığınız sürece, herhangi bir fark yaratmaz. Daha ANSI_WARNINGSsonraki sürümlerde de belirtilmiştir ve dizinlenmiş görünümler gibi şeyler iyi sonuç verir.
Martin Smith

.NET'in ARITHABORT'u devre dışı bırakmak için kodlanmış olmadığını unutmayın . Ssms ayarlayarak varsayılan üzerinde . Net sadece sunucu / veritabanı varsayılanlarını bağlayıp kullanıyor. MS Connect'te, SSMS'nin varsayılan davranışından şikayet eden kullanıcılar hakkındaki sorunları bulabilirsiniz. Not ARITHABORT doc sayfasındaki uyarı .
Bacon,

2

Birisine zaman kazandırırsa, benim durumumda (Entity Framework Çekirdek 2.0.3, ASP.Net Çekirdek API, SQL Server 2008 R2):

  1. EF Core 2.0'da Interceptors yok (Yakında 2.1'de satışa sunulacağını düşünüyorum)
  2. Ne küresel DB ayarını değiştirmek ne de ayarını user_optionsyapmak benim için kabul edilebilirdi (işe yarıyorlar - test ettim) ama diğer uygulamaları etkileme riskini alamadım.

EF Core'dan SET ARITHABORT ON;en üstte olan bir geçici sorgu çalışmaz.

Sonunda, benim için işe yarayan çözüm şuydu: Ham bir sorgu olarak adlandırılan saklı yordamın, noktalı virgülle SETayrılmadan önceki seçenekle birleştirilmesi EXEC:

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

İlginç. EF Core ile çalışmanın bu nüanslarını gönderdiğiniz için teşekkür ederiz. Merak ettim: burada ne yapıyorsunuz temelde cevabımdaki Alternatif yaklaşımlar bölümünün ELSE alt bölümünde bahsettiğim sarmalayıcı seçeneği ? Merak ediyordum çünkü cevabımdaki diğer önerilerden bahsettiniz ya çalışmayan ya da diğer kısıtlamalar nedeniyle uygulanabilir değil, ama sarmalayıcı seçeneğinden bahsetmedim.
Solomon Rutzky,

@SolomonRutzky, bu seçeneğe eşdeğerdir; nüanslı olarak saklı bir prosedürü uygulamakla sınırlıdır. Benim durumumda SET OPTION ile bir ham güncelleme sorgusu önceden eklersem (elle veya sarmalayıcı aracılığıyla) çalışmaz. SET OPTION seçeneğini saklı yordamın içine koyarsam, çalışmaz. Tek seçenek SET OPTION ve ardından aynı serideki EXEC saklı prosedürünü yapmaktı. Bu şekilde, sarıcı yapmak yerine belirli bir aramayı özelleştirmeyi seçtim. Yakında SQLServer 2016'ya güncelleme yapacağız ve bunu temizleyebilirim. Belirli senaryoları atmakta yardımcı olduysa, cevabınız için teşekkürler.
Chris Amelinckx

0

EF6 için Solomon Rutzy cevabı üzerine inşa:

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)
        ...

Bu kullanımlar System.Data.Common's DbCommandyerine SqlCommandve DbConnectionyerine SqlConnection.

Bir SQL Profiler izi SET ARITHABORT ON, bağlantı açıldığında, işlem sırasında başka bir komut çalıştırılmadan önce gönderilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.