Pozornie preferowane podejście
Miałem wrażenie, że następujące zostały już przetestowane przez innych, zwłaszcza na podstawie niektórych komentarzy. Ale moje testy pokazują, że te dwie metody rzeczywiście działają na poziomie bazy danych, nawet podczas łączenia przez .NET SqlClient
. Zostały one przetestowane i zweryfikowane przez innych.
Cały serwer
Możesz ustawić ustawienie konfiguracji serwera opcji użytkownika tak, aby było to, co jest obecnie bitowo edytowane OR
przy pomocy 64 (wartość dla ARITHABORT
). Jeśli nie użyjesz bitowej funkcji OR ( |
), ale zamiast tego wykonasz proste przypisanie ( =
), usuniesz wszystkie inne istniejące już opcje.
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
Na poziomie bazy danych
Można to ustawić dla każdej bazy danych za pomocą ZMIEŃ ZESTAW BAZY DANYCH :
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;
Alternatywne podejścia
Niezbyt dobra wiadomość jest taka, że dużo szukałem w tym temacie, ale okazało się, że przez lata wiele innych szukało w tym temacie i nie ma sposobu, aby skonfigurować zachowanie z SqlClient
. Niektóre dokumenty MSDN sugerują, że można to zrobić za pomocą ConnectionString, ale nie ma słów kluczowych, które pozwoliłyby na zmianę tych ustawień. Inny dokument sugeruje, że można go zmienić za pomocą Menedżera konfiguracji sieci / Menedżera konfiguracji, ale nie wydaje się to również możliwe. Dlatego i raczej niestety trzeba będzie wykonać SET ARITHABORT ON;
ręcznie. Oto kilka sposobów na rozważenie:
JEŚLI używasz Entity Framework 6 (lub nowszego), możesz spróbować:
Użyj Database.ExecuteSqlCommand : context.Database.ExecuteSqlCommand("SET ARITHABORT ON;");
Najlepiej byłoby to wykonać raz, po otwarciu połączenia DB, a nie dla każdego zapytania.
Utwórz przechwytywacz za pomocą:
To pozwoli Ci zmodyfikować SQL, zanim zostanie wykonane, w którym to przypadku można po prostu poprzedzić je: SET ARITHABORT ON;
. Minusem jest to, że będzie to za każdym zapytaniu, chyba że przechowywanie zmiennej lokalnej uchwycić stan, czy nie został on wykonany test i że za każdym razem (co naprawdę nie jest dużo dodatkowej pracy, ale przy użyciu ExecuteSqlCommand
jest prawdopodobnie łatwiej).
Każdy z nich pozwoli ci obsłużyć to w jednym miejscu bez zmiany istniejącego kodu.
ELSE można utworzyć metodę opakowania, która to robi, podobnie jak:
public static SqlDataReader ExecuteReaderWithSetting(SqlCommand CommandToExec)
{
CommandToExec.CommandText = "SET ARITHABORT ON;\n" + CommandToExec.CommandText;
return CommandToExec.ExecuteReader();
}
a następnie po prostu zmień bieżące _Reader = _Command.ExecuteReader();
odniesienia na_Reader = ExecuteReaderWithSetting(_Command);
.
Dzięki temu ustawienie może być obsługiwane w jednym miejscu, wymagając jedynie minimalnych i uproszczonych zmian kodu, które można w większości wykonać za pomocą funkcji Znajdź i zamień.
Jeszcze lepiej ( Else Part 2), ponieważ jest to ustawienie poziomu połączenia, nie trzeba go wykonywać dla każdego wywołania SqlCommand.Execute __ (). Zamiast tworzyć opakowanie dla ExecuteReader()
, utwórz opakowanie dla 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 następnie wystarczy zastąpić istniejące _Connection.Open();
odniesieniaOpenAndSetArithAbort(_Connection);
.
Oba powyższe pomysły można zaimplementować w większym stylu OO, tworząc klasę rozszerzającą SqlCommand lub SqlConnection.
Lub jeszcze lepiej ( Else Part 3), możesz utworzyć moduł obsługi zdarzeń dla Connection StateChange i ustawić właściwość, gdy połączenie zmieni się z Closed
na 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();
}
}
}
W tym miejscu wystarczy dodać następujące elementy do każdego miejsca, w którym utworzono SqlConnection
instancję:
_Connection.StateChange += new StateChangeEventHandler(OnStateChange);
Nie są wymagane żadne zmiany w istniejącym kodzie. Właśnie wypróbowałem tę metodę w małej aplikacji na konsolę, testując, drukując wynik SELECT SESSIONPROPERTY('ARITHABORT');
. Zwraca 1
, ale jeśli wyłączę moduł obsługi zdarzeń, zwraca 0
.
Dla kompletności, oto kilka rzeczy, które nie działają (albo wcale, albo nie tak skutecznie):
- Wyzwalacze logowania : Wyzwalacze, nawet jeśli działają w tej samej sesji, a nawet jeśli działają w ramach jawnie rozpoczętej transakcji, są nadal podprocesem, a zatem i jej ustawieniami (
SET
polecenia, lokalne tabele tymczasowe itp.) Są dla niej lokalne i nie przetrwają koniec tego podprocesu.
- Dodawanie
SET ARITHABORT ON;
na początku każdej procedury składowanej:
- wymaga to dużo pracy w przypadku istniejących projektów, zwłaszcza gdy rośnie liczba procedur przechowywanych
- nie pomaga to w zapytaniach ad hoc
SELECT DATABASEPROPERTYEX('{database_name}', 'IsArithmeticAbortEnabled');
zwróceniu 1 sys.dm_exec_sessions pokazuje arithabort wyłączony, chociaż nie widzę żadnych wyraźnych zestawów w Profiler. Dlaczego miałoby to być?opcja 1
Oprócz rozwiązania Sankar , ustawienie arytmetycznego ustawienia przerwania na poziomie serwera dla wszystkich połączeń będzie działać:
Począwszy od SQL 2014 zaleca się włączenie wszystkich połączeń:
Wydawałoby się to idealnym rozwiązaniem.
Opcja 2
Jeśli opcja 1 nie jest wykonalna i używasz procedur przechowywanych dla większości swoich wywołań SQL (które powinieneś zobaczyć Procedury składowane vs. Inline SQL ), po prostu włącz tę opcję w każdej odpowiedniej procedurze przechowywanej:
Uważam, że najlepszym prawdziwym rozwiązaniem jest po prostu edycja kodu, ponieważ jest on błędny, a każda inna poprawka jest jedynie obejściem.
źródło
set ArithAbort off
. Miałem nadzieję na coś, co można zrobić po stronie .net / C #. Wystawiłem nagrodę, ponieważ widziałem zalecenie.NIE jestem tutaj ekspertem, ale możesz spróbować czegoś takiego jak poniżej.
Ref: http://social.msdn.microsoft.com/Forums/en-US/transactsql/thread/d9e3e8ba-4948-4419-bb6b-dd5208bd7547/
źródło
Nie ma ustawienia, które zmusiłoby SqlClient do zawsze włączania ARITHABORT, musisz to ustawić zgodnie z opisem.
Co ciekawe z dokumentacji Microsoft dla SET ARITHABORT : -
A jednak połączenie .Net jest ustawione na stałe, aby domyślnie to wyłączyć?
Kolejnym punktem jest konieczność zachowania ostrożności podczas diagnozowania problemów z wydajnością przy tym ustawieniu. Różne opcje zestawu będą skutkować różnymi planami zapytań dla tego samego zapytania. W kodzie .Net może wystąpić problem z wydajnością (USTAW ARITHABORT WYŁ), a jednak po uruchomieniu tego samego zapytania TSQL w SSMS (domyślnie USTAW ARITHABORT) może być w porządku. Wynika to z faktu, że plan zapytań .Net nie zostanie ponownie użyty i wygenerowany zostanie nowy plan. Może to potencjalnie wyeliminować na przykład problem z wąchaniem parametrów i dać znacznie lepszą wydajność.
źródło
ANSI_WARNINGS
w późniejszych wersjach i rzeczy takie jak widoki indeksowane działają dobrze.Jeśli oszczędza to trochę czasu, w moim przypadku (Entity Framework Core 2.0.3, ASP.Net Core API, SQL Server 2008 R2):
user_options
było dla mnie do zaakceptowania (działają - testowałem), ale nie mogłem ryzykować wpływu na inne aplikacje.Kwerenda ad hoc z EF Core z
SET ARITHABORT ON;
na górze, nie działa.Wreszcie, rozwiązaniem, które zadziałało dla mnie było: połączenie procedury składowanej, zwanej surowym zapytaniem z
SET
opcją przedEXEC
oddzieleniem średnikiem, jak poniżej:źródło
Opierając się na odpowiedzi Solomona Rutzy'ego dla EF6:
To używa
System.Data.Common
'DbCommand
zamiast 'SqlCommand
iDbConnection
zamiastSqlConnection
.Śledzenie SQL Profiler potwierdza, że
SET ARITHABORT ON
jest wysyłane, gdy połączenie zostanie otwarte, przed wykonaniem jakichkolwiek innych poleceń w transakcji.źródło