SqlClient를 ARITHABORT ON으로 기본 설정


32

먼저해야 할 것 : 호환성 수준이 80 인 데이터베이스와 함께 MS SQL Server 2008을 사용하고 .Net 's에 연결하고 있습니다 System.Data.SqlClient.SqlConnection.

성능상의 이유로 인덱스 된 뷰를 만들었습니다. 결과적으로 뷰에서 참조 된 테이블에 대한 업데이트는로 수행해야합니다 ARITHABORT ON. 그러나 프로파일 러는 SqlClient가에 연결 중임을 표시 ARITHABORT OFF하므로 해당 테이블에 대한 업데이트가 실패합니다.

SqlClient를 사용하기위한 중앙 구성 설정이 ARITHABORT ON있습니까? 내가 찾은 최선의 방법은 연결을 열 때마다 수동으로 실행하는 것이지만 기존 코드베이스를 업데이트하면 상당히 큰 작업이므로 더 나은 방법을 찾고 싶습니다.

답변:


28

겉보기에 선호되는 접근법

나는 다른 사람들, 특히 일부 의견을 바탕으로 이미 다음과 같은 테스트를 받았다는 인상을 받았습니다. 그러나 내 테스트에 따르면이 두 가지 방법은 .NET을 통해 연결할 때도 실제로 DB 수준에서 작동합니다 SqlClient. 이것들은 다른 사람들에 의해 테스트되고 검증되었습니다.

서버 전체

당신은 설정할 수있는 사용자 옵션 은 비트 단위 현재 무엇이든 할 서버 구성 설정을 OR64 (값과 에드 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;수동으로 실행 해야합니다. 고려해야 할 몇 가지 방법이 있습니다.

경우 당신이 엔티티 프레임 워크 6 (이상)을 사용하고, 당신도 시도 할 수 있습니다 :

  • Database.ExecuteSqlCommand 사용 : context.Database.ExecuteSqlCommand("SET ARITHABORT ON;");
    이상적으로는 각 쿼리마다가 아닌 DB 연결을 연 후에 한 번 실행됩니다.

  • 다음 중 하나를 통해 인터셉터 를 작성하십시오 .

    이를 통해 SQL을 실행하기 전에 수정할 수 있습니다.이 경우 단순히 접두사로 다음을 지정할 수 있습니다 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);.

위의 두 가지 아이디어는 SqlCommand 또는 SqlConnection을 확장하는 클래스를 만들어 더 많은 OO 스타일로 구현할 수 있습니다.

또는 더 나은 아직 ( 그 밖에 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를 사용하여 간단한 데이터베이스를 작성하고 0을 사용하여 테이블을 작성하고 간단한 .net 클라이언트에서 읽을 수 있도록 테스트했습니다. .net SqlClient는 SQL 프로파일 러의 로그인에서 ARITHABORT를 끄고 ANSI_WARNINGS를 켜는 것으로 표시했으며 예상대로 0으로 나누면서 쿼리에 실패했습니다. 이것은 db 수준 플래그를 설정하는 기본 솔루션이 .net SqlClient의 기본값을 변경하는 데 작동하지 않는 것으로 보입니다.
마이크

그러나 서버 전체의 user_options 설정이 작동하는지 확인할 수 있습니다.
마이크

또한 SELECT DATABASEPROPERTYEX('{database_name}', 'IsArithmeticAbortEnabled');1 을 반환하면 sys.dm_exec_sessions는 arithabort off를 표시하지만 Profiler에는 명시적인 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 # 측에서 수행 할 수있는 것을 기대하고있었습니다. 추천서를 보았 기 때문에 현상금을 지급했습니다.
Henrik Staun Poulsen 2016 년

1
.net / C # 쪽은 Sankar가 다루는 것이므로 이것들 만이 유일한 옵션입니다.
LowlyDBA

옵션 1을 시도했지만 아무런 효과가 없었습니다. 새 세션은 여전히 ​​arithabort = 0을 갖는 것으로 표시됩니다. 잠재적 인 문제를 극복하려고 시도하면서 아무런 문제가 없습니다.
Mark Freeman

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인덱싱 된 뷰 잘 작동 등의 이후 버전과 일에에.
Martin Smith

.Net은 ARITHABORT를 끄도록 하드 코딩되어 있지 않습니다 . SSMS는 기본적으로 설정되어 있습니다 . .Net은 서버 / 데이터베이스 기본값을 연결하고 사용 중입니다. SSMS의 기본 동작에 대해 불평하는 사용자에 대한 MS Connect의 문제를 찾을 수 있습니다. ARITHABORT 문서 페이지의 경고에 유의 하십시오 .
베이컨 비트

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 하위 섹션 에서 언급 한 래퍼 옵션 입니까? 나는 당신이 내 대답에서 다른 제안들이 작동하지 않거나 다른 제약으로 인해 실행 가능하지 않다는 것을 언급했지만 래퍼 옵션을 언급하지 않았기 때문에 궁금합니다.
Solomon Rutzky

@SolomonRutzky는 해당 옵션과 동일하며 저장 프로 시저 실행으로 제한됩니다. 필자의 경우 원시 업데이트 쿼리에 SET OPTION (수동으로 또는 래퍼를 통해) 접두사를 사용하면 작동하지 않습니다. 저장 프로 시저 안에 SET OPTION을 넣으면 작동하지 않습니다. 유일한 방법은 SET OPTION과 EXEC 저장 프로 시저를 동일한 배치로 수행하는 것입니다. 이것이 래퍼를 만드는 대신 특정 호출을 사용자 정의하도록 선택한 방법입니다. 곧 SQL Server 2016으로 업데이트하고이를 정리할 수 있습니다. 특정 시나리오를 삭제하는 데 도움이 되었으면 답변 해 주셔서 감사합니다.
Chris Amelinckx

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.

SET ARITHABORT ON트랜잭션에서 다른 명령이 실행되기 전에 연결이 열리면 SQL 프로파일 러 추적 이 전송됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.