ARKABORT ON पर SqlClient को डिफ़ॉल्ट बनाएं


32

पहली चीजें पहली: मैं संगतता स्तर 80 पर एक डेटाबेस के साथ एमएस SQL ​​सर्वर 2008 का उपयोग कर रहा हूं, और इसे .Net के साथ कनेक्ट कर रहा हूं System.Data.SqlClient.SqlConnection

प्रदर्शन कारणों से मैंने अनुक्रमणित दृश्य बनाया है। नतीजतन, दृश्य में संदर्भित तालिकाओं के अपडेट के साथ किया जाना चाहिए ARITHABORT ON। हालाँकि, प्रोफाइलर दिखाता है कि SqlClient के साथ जुड़ रहा है ARITHABORT OFF, इसलिए उन तालिकाओं के लिए अद्यतन विफल हो रहे हैं।

SqlClient का उपयोग करने के लिए एक केंद्रीय कॉन्फ़िगरेशन सेटिंग है ARITHABORT ON? मुझे जो सबसे अच्छा मिल रहा है, वह मैन्युअल रूप से निष्पादित करने के लिए है कि हर बार एक कनेक्शन खोला जाता है, लेकिन ऐसा करने के लिए मौजूदा कोड आधार को अपडेट करना एक काफी बड़ा काम होगा इसलिए मैं एक बेहतर तरीका खोजने का इच्छुक हूं।

जवाबों:


28

लगता है पसंदीदा तरीका

मैं इस धारणा के तहत था कि निम्नलिखित का परीक्षण पहले से ही दूसरों द्वारा किया गया था, विशेष रूप से कुछ टिप्पणियों के आधार पर। लेकिन मेरे परीक्षण से पता चलता है कि ये दो विधियां वास्तव में DB स्तर पर काम करती हैं, यहां तक ​​कि .NET के माध्यम से कनेक्ट होने पर भी SqlClientइनका परीक्षण और सत्यापन अन्यों द्वारा किया गया है।

सर्वर चौड़ा

आप उपयोगकर्ता विकल्प सर्वर कॉन्फ़िगरेशन सेटिंग को सेट कर सकते हैं जो कि वर्तमान में OR64 -बिट (मूल्य के लिए ARITHABORT) के साथ बिट-वार एड है । यदि आप बिट-वार या ( |) का उपयोग नहीं करते हैं, लेकिन इसके बजाय एक सीधा असाइनमेंट ( =) करते हैं तो आप किसी भी अन्य मौजूदा विकल्प को मिटा देंगे।

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

डाटाबेस स्तरीय

इसे प्रति डेटाबेस सेट के माध्यम से प्रति-डेटाबेस सेट किया जा सकता है :

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;मैन्युअल रूप से निष्पादित करने की आवश्यकता होगी । विचार करने के कुछ तरीके इस प्रकार हैं:

यदि आप एंटिटी फ्रेमवर्क 6 (या नए) का उपयोग कर रहे हैं, तो आप या तो कोशिश कर सकते हैं:

  • Database.ExecuteSqlCommand का उपयोग करें : context.Database.ExecuteSqlCommand("SET ARITHABORT ON;");
    आदर्श रूप से यह DB कनेक्शन खोलने के बाद एक बार निष्पादित किया जाएगा, और प्रत्येक क्वेरी के लिए नहीं।

  • या तो एक इंटरसेप्टर बनाएं :

    यह निष्पादित होने से पहले आपको एसक्यूएल को संशोधित करने की अनुमति देगा, जिस स्थिति में आप बस इसके साथ उपसर्ग कर सकते हैं SET ARITHABORT ON;:। यहां नकारात्मक पक्ष यह है कि यह प्रत्येक क्वेरी के अनुसार होगा, जब तक कि आप स्थानीय चर को स्टोर नहीं करते हैं या नहीं, इस स्थिति को पकड़ने के लिए निष्पादित किया गया है या नहीं और उस समय के लिए परीक्षण करें (जो वास्तव में इतना अतिरिक्त काम नहीं है, लेकिन इसका उपयोग करना ExecuteSqlCommandहै) शायद आसान)।

उनमें से कोई भी आपको किसी मौजूदा कोड को बदलने के बिना एक स्थान पर इसे संभालने की अनुमति देगा।

ईएलएसई , आप एक आवरण विधि बना सकते हैं जो ऐसा करती है:

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

उपरोक्त दोनों विचारों को एक वर्ग बनाकर अधिक OO शैली में लागू किया जा सकता है जो SqlCommand या SqlConnection तक फैला हुआ है।

या बेहतर अभी तक ( वरना भाग 3), आप कनेक्शन StateChange के लिए एक ईवेंट हैंडलर बनाने और इसे गुण सेट कर सकते हैं जब से कनेक्शन परिवर्तन 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


पूर्णता की खातिर, यहां कुछ चीजें हैं जो काम नहीं करती हैं (या तो बिल्कुल भी या प्रभावी रूप से नहीं):

  • लोगोन ट्रिगर : ट्रिगर, एक ही सत्र में चलने के दौरान, और भले ही स्पष्ट रूप से शुरू किए गए लेनदेन के भीतर चल रहा हो, फिर भी एक उप-प्रक्रिया है और इसलिए इसकी सेटिंग्स ( SETकमांड, स्थानीय अस्थायी टेबल, आदि) इसके लिए स्थानीय हैं और जीवित नहीं हैं उस उप-प्रक्रिया का अंत।
  • SET ARITHABORT ON;प्रत्येक संग्रहीत कार्यविधि की शुरुआत में जोड़ना :
    • इसके लिए मौजूदा परियोजनाओं के लिए बहुत काम करने की आवश्यकता है, खासकर जब संग्रहीत प्रक्रियाओं की संख्या बढ़ जाती है
    • यह तदर्थ प्रश्नों की मदद नहीं करता है

मैंने अभी-अभी ARITHABORT और ANSI_WARNINGS दोनों के साथ एक सरल डेटाबेस बनाने का परीक्षण किया, इसमें शून्य के साथ एक तालिका बनाई और इसे पढ़ने के लिए एक साधारण .net क्लाइंट बनाया। .Net SqlClient ने ARLABORT को बंद करके और ANSI_WARNINGS को sql प्रोफाइलर में लॉगिन में दिखाया और साथ ही साथ क्वेरी को विभाजित करके शून्य के रूप में विफल कर दिया। ऐसा लगता है कि db स्तर झंडे स्थापित करने का पसंदीदा समाधान .net SqlClient के लिए डिफ़ॉल्ट बदलने के लिए काम नहीं करेगा।
माइक

हालांकि सर्वर-वाइड user_options सेट करने की पुष्टि कर सकते हैं।
माइक

मैं यह भी देख रहा हूं कि SELECT DATABASEPROPERTYEX('{database_name}', 'IsArithmeticAbortEnabled');1 लौटने के साथ , sysinos_exec_session arithabort को बंद कर देता है, हालांकि मुझे Profiler में कोई स्पष्ट SET नहीं दिखाई देता है। ऐसा क्यों होगा?
andrew.rockwell

6

विकल्प 1

शंकर के समाधान के अलावा , सभी कनेक्शन के लिए सर्वर स्तर पर अंकगणितीय गर्भपात सेटिंग काम करेगी:

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

एसक्यूएल 2014 के अनुसार यह सभी कनेक्शनों के लिए होने की सिफारिश की गई है :

आपको अपने लॉगऑन सत्रों में हमेशा ARITHABORT को चालू करना चाहिए। प्रदर्शन के मुद्दों के लिए क्वेरी अनुकूलन को नकारात्मक रूप से प्रभावित कर सकता है।

तो यह आदर्श समाधान प्रतीत होगा।

विकल्प 2

यदि विकल्प 1 व्यवहार्य नहीं है और आप अपनी अधिकांश SQL कॉल के लिए संग्रहीत कार्यविधियों का उपयोग करते हैं (जो आपको चाहिए, संग्रहीत कार्यविधियाँ बनाम इनलाइन SQL देखें ) तो बस प्रत्येक प्रासंगिक संग्रहीत कार्यविधि में विकल्प को सक्षम करें:

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

मेरा मानना ​​है कि यहां सबसे अच्छा वास्तविक समाधान केवल अपने कोड को संपादित करना है, क्योंकि यह गलत है और कोई भी अन्य समाधान केवल एक समाधान है।


मुझे नहीं लगता कि यह SQL सर्वर के लिए इसे सेट करने में मदद करता है, जब .net कनेक्शन के साथ शुरू होता है set ArithAbort off। मैं कुछ के लिए उम्मीद कर रहा था जो कि .net / C # की तरफ हो सकता है। मैंने इनाम रखा, क्योंकि मैंने सिफारिश देखी थी।
हेनरिक स्टॉउन पॉल्सेन

1
.Net / C # साइड वह है जिसे सांकड़ ने कवर किया है, इसलिए ये बहुत ही एकमात्र विकल्प हैं।
लोवलीबा

मैंने विकल्प 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();

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


हां, इसका मतलब है कि मैं इसे मैन्युअल रूप से निष्पादित कर रहा हूं। बात यह है कि मैं जिस कोडबेस के साथ काम कर रहा हूं, उसने डीबी एक्सेस लेयर में आने पर काफी तकनीकी ऋण अर्जित किया है, इसलिए मुझे इसे इस तरह से करने के लिए कुछ सौ तरीकों को रिफलेक्टर करना होगा।
पीटर टेलर

2

SqlClient को हमेशा ARITHABORT सेट करने के लिए बाध्य करने के लिए कोई सेटिंग नहीं है, आपको यह वर्णन करते हुए सेट करना होगा।

दिलचस्प रूप से SET ARITHABORT के लिए Microsoft दस्तावेज़ से : -

आपको अपने लॉगऑन सत्रों में हमेशा ARITHABORT को चालू करना चाहिए। प्रदर्शन के मुद्दों के लिए क्वेरी अनुकूलन को नकारात्मक रूप से प्रभावित कर सकता है।

और फिर भी .Net कनेक्शन को डिफ़ॉल्ट रूप से सेट करना मुश्किल है?

एक अन्य बिंदु के रूप में, आपको इस सेटिंग के साथ प्रदर्शन समस्याओं का निदान करते समय बहुत सावधान रहना होगा। अलग-अलग सेट विकल्पों के परिणामस्वरूप एक ही क्वेरी के लिए अलग-अलग क्वेरी प्लान होंगे। आपका .Net कोड एक प्रदर्शन समस्या (SET ARITHABORT OFF) का अनुभव कर सकता है और फिर भी जब आप SSMS में समान TSQL क्वेरी चलाते हैं (डिफ़ॉल्ट रूप से सेट करें) तो यह ठीक हो सकता है। ऐसा इसलिए है क्योंकि .Net क्वेरी योजना का पुन: उपयोग नहीं किया जाएगा और एक नई योजना उत्पन्न होगी। यह संभावित रूप से उदाहरण के लिए एक सूँघने की समस्या को खत्म कर सकता है और बेहतर प्रदर्शन दे सकता है।


1
@HenrikStaunPoulsen - जब तक आप 2000 (या 2000 संगतता स्तर) का उपयोग कर रहे हैं तब तक कोई फर्क नहीं पड़ता है। इसे ANSI_WARNINGSबाद के संस्करणों में अनुक्रमित किया गया है और अनुक्रमित विचार जैसी चीजें ठीक काम करती हैं।
मार्टिन स्मिथ

ध्यान दें कि। ARETABORT को बंद करने के लिए .Net हार्ड-कोडेड नहीं है। SSMS इसे सेट करने में चूक करता है । .Net बस सर्वर / डेटाबेस डिफॉल्ट को कनेक्ट और उपयोग कर रहा है। आप SSMS के डिफ़ॉल्ट व्यवहार के बारे में शिकायत करने वाले उपयोगकर्ताओं के बारे में MS Connect पर समस्याएँ पा सकते हैं। ARITHABORT डॉक्टर पृष्ठ पर चेतावनी पर ध्यान दें ।
बेकन बिट्स

2

यदि यह मेरे मामले में (एंटिटी फ्रेमवर्क कोर 2.0.3, ASP.Net कोर एपीआई, SQL सर्वर 2008 R2) किसी को कुछ समय बचाता है:

  1. EF Core 2.0 पर कोई इंटरसेप्टर नहीं हैं (मुझे लगता है कि वे जल्द ही 2.1 पर उपलब्ध होंगे)
  2. न तो वैश्विक डीबी सेटिंग बदलना और न ही सेटिंग user_optionsमेरे लिए स्वीकार्य थी (वे काम करते हैं - मैंने परीक्षण किया) लेकिन मैं अन्य अनुप्रयोगों को प्रभावित करने का जोखिम नहीं उठा सकता था।

ईएफ कोर से एक तदर्थ क्वेरी, SET ARITHABORT ON;शीर्ष पर काम नहीं करती है।

अंत में, जो समाधान मेरे लिए काम करता था वह था: एक संग्रहित प्रक्रिया का संयोजन, एक अर्धविराम द्वारा अलग किए जाने SETसे पहले विकल्प के साथ एक कच्ची क्वेरी के रूप में कहा जाता है EXEC:

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

दिलचस्प। EF Core के साथ काम करने की इन बारीकियों को पोस्ट करने के लिए धन्यवाद। बस जिज्ञासु: आप यहाँ क्या कर रहे हैं अनिवार्य रूप से आवरण विकल्प मैंने अपने उत्तर में वैकल्पिक दृष्टिकोण अनुभाग के ईएलएसई उप-खंड में उल्लेख किया है ? मैं सिर्फ इसलिए सोच रहा था क्योंकि आपने मेरे जवाब में अन्य सुझावों का उल्लेख किया है या तो काम नहीं कर रहा है या अन्य बाधाओं के कारण व्यवहार्य नहीं है, लेकिन आवरण विकल्प का उल्लेख नहीं किया है।
सोलोमन रटज़की

@SolomonRutzky यह उस विकल्प के बराबर है, इस बारीकियों के साथ कि यह एक संग्रहीत प्रक्रिया को निष्पादित करने तक सीमित है। मेरे मामले में अगर मैं SET विकल्प (मैन्युअल रूप से या आवरण के माध्यम से) के साथ एक कच्चा अपडेट क्वेरी उपसर्ग करता हूं तो यह काम नहीं करता है। यदि मैं SET विकल्प को संग्रहीत कार्यविधि के अंदर रखता हूं, तो यह काम नहीं करता है। एकमात्र तरीका SET OPTION करना था जिसके बाद उसी बैच में EXEC स्टोरेज प्रक्रिया का पालन किया गया। जिस तरह से मैंने रैपर बनाने के बजाय विशिष्ट कॉल को अनुकूलित करने का विकल्प चुना। जल्द ही हम SQLServer 2016 को अपडेट करेंगे और मैं इसे साफ कर सकता हूं। आपके जवाब के लिए धन्यवाद। यदि विशिष्ट परिदृश्यों को छोड़ने में मददगार था।
क्रिस ऐमेलिनक्स

0

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.Common'एस DbCommandके बजाय SqlCommand, और DbConnectionके बजाय SqlConnection

एक SQL Profiler ट्रेस पुष्टि करता है, SET ARITHABORT ONजब कनेक्शन खुलता है, तो लेनदेन में किसी भी अन्य कमांड को निष्पादित करने से पहले भेजा जाता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.