it-swarm.asia

Jadikan SqlClient sebagai default untuk ARITHABORT ON

Hal pertama yang pertama: Saya menggunakan MS SQL Server 2008 dengan database pada tingkat kompatibilitas 80, dan menghubungkannya dengan. Net's System.Data.SqlClient.SqlConnection.

Untuk alasan kinerja, saya telah membuat tampilan yang diindeks. Akibatnya, pembaruan ke tabel yang direferensikan dalam tampilan perlu dilakukan dengan ARITHABORT ON. Namun, profiler menunjukkan bahwa SqlClient terhubung dengan ARITHABORT OFF, jadi pembaruan ke tabel tersebut gagal.

Apakah ada pengaturan konfigurasi pusat untuk membuat SqlClient menggunakan ARITHABORT ON? Yang terbaik yang bisa saya temukan adalah dengan mengeksekusi secara manual bahwa setiap kali koneksi dibuka, tetapi memperbarui basis kode yang ada untuk melakukan ini akan menjadi tugas yang cukup besar jadi saya ingin menemukan cara yang lebih baik.

32
Peter Taylor

Pendekatan yang lebih disukai

Saya mendapat kesan bahwa yang berikut ini sudah diuji oleh orang lain, terutama berdasarkan beberapa komentar. Tetapi pengujian saya menunjukkan bahwa kedua metode ini memang bekerja pada tingkat DB, bahkan ketika menghubungkan melalui .NET SqlClient. Ini telah diuji dan diverifikasi oleh orang lain.

Server-lebar

Anda dapat mengatur opsi pengguna pengaturan konfigurasi server menjadi apa pun yang saat ini bit-wise ORed dengan 64 (nilai untuk ARITHABORT). Jika Anda tidak menggunakan bit-wise OR (|) Tetapi sebaliknya lakukan penugasan langsung (=) Maka Anda akan menghapus semua opsi lain yang ada sudah diaktifkan.

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

Level basis data

Ini dapat diatur per-basis data melalui 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;

Pendekatan alternatif

Berita yang tidak begitu baik adalah bahwa saya telah melakukan banyak pencarian pada topik ini, hanya untuk menemukan bahwa selama bertahun-tahun banyak orang lain telah melakukan banyak pencarian pada topik ini, dan tidak ada cara untuk mengkonfigurasi perilaku dari SqlClient. Beberapa dokumentasi MSDN menyiratkan bahwa hal itu dapat dilakukan melalui ConnectionString, tetapi tidak ada Kata Kunci yang memungkinkan untuk mengubah pengaturan ini. Dokumen lain menyiratkan hal itu dapat diubah melalui Konfigurasi Jaringan/Manajer Konfigurasi Klien, tetapi tampaknya juga tidak mungkin. Karenanya, dan sayangnya, Anda harus menjalankan SET ARITHABORT ON; Secara manual. Berikut adalah beberapa cara untuk dipertimbangkan:

[~ # ~] jika [~ # ~] Anda menggunakan Entity Framework 6 (atau yang lebih baru), Anda dapat mencoba:

  • Gunakan Database.ExecuteSqlCommand : context.Database.ExecuteSqlCommand("SET ARITHABORT ON;");
    Idealnya ini akan dieksekusi sekali, setelah membuka koneksi DB, dan tidak per setiap permintaan.

  • Buat interseptor melalui:

    Ini akan memungkinkan Anda untuk memodifikasi SQL sebelum dieksekusi, dalam hal ini Anda cukup awalan dengan: SET ARITHABORT ON;. Kelemahan di sini adalah bahwa itu akan menjadi per setiap permintaan, kecuali Anda menyimpan variabel lokal untuk menangkap status apakah itu telah dieksekusi dan diuji untuk itu setiap kali (yang sebenarnya bukan pekerjaan ekstra, tetapi menggunakan ExecuteSqlCommand mungkin lebih mudah).

Salah satu dari mereka akan memungkinkan Anda untuk menangani ini di satu tempat tanpa mengubah kode yang ada.

[~ # ~] lain [~ # ~] , Anda dapat membuat metode pembungkus yang melakukan ini, mirip dengan:

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

  return CommandToExec.ExecuteReader();
}

dan kemudian hanya mengubah referensi _Reader = _Command.ExecuteReader(); saat ini menjadi _Reader = ExecuteReaderWithSetting(_Command);.

Melakukan hal ini juga memungkinkan pengaturan ditangani dalam satu lokasi sementara hanya memerlukan perubahan kode yang minimal dan sederhana yang sebagian besar dapat dilakukan melalui Temukan & Ganti.

Lebih baik lagi ( Lain Bagian 2), karena ini adalah pengaturan level koneksi, tidak perlu dieksekusi per setiap panggilan SqlCommand.Execute __ (). Jadi alih-alih membuat wrapper untuk ExecuteReader(), buat wrapper untuk 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;
}

Dan kemudian ganti referensi _Connection.Open(); yang ada menjadi OpenAndSetArithAbort(_Connection);.

Kedua ide di atas dapat diimplementasikan dalam gaya yang lebih OO dengan membuat Kelas yang meluas baik SqlCommand atau SqlConnection.

Atau Lebih Baik lagi ( Lain Bagian 3), Anda dapat membuat pengendali acara untuk Connection StateChange dan mengaturnya properti ketika koneksi berubah dari Closed ke Open sebagai berikut:

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

Dengan itu, Anda hanya perlu menambahkan yang berikut ke setiap tempat di mana Anda membuat instance SqlConnection:

_Connection.StateChange += new StateChangeEventHandler(OnStateChange);

Tidak diperlukan perubahan kode yang ada. Saya baru saja mencoba metode ini di aplikasi konsol kecil, menguji dengan mencetak hasil SELECT SESSIONPROPERTY('ARITHABORT');. Ia mengembalikan 1, Tetapi jika saya menonaktifkan Event Handler, ia mengembalikan 0.


Demi kelengkapan, berikut adalah beberapa hal yang tidak berfungsi (baik sama sekali atau tidak seefektif):

  • Pemicu Logon : Pemicu, bahkan ketika berjalan di sesi yang sama, dan bahkan jika berjalan dalam transaksi yang dimulai secara eksplisit, masih merupakan sub-proses dan karenanya pengaturannya (SET perintah, lokal tabel sementara, dll) bersifat lokal dan tidak bertahan pada akhir sub-proses itu.
  • Menambahkan SET ARITHABORT ON; Ke awal setiap prosedur tersimpan:
    • ini membutuhkan banyak pekerjaan untuk proyek yang ada, terutama karena jumlah prosedur tersimpan meningkat
    • ini tidak membantu permintaan ad hoc
29
Solomon Rutzky

Pilihan 1

Selain solusi Sankar , pengaturan pengaturan aborsi aritmatika di tingkat server untuk semua koneksi akan berfungsi:

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

Pada SQL 2014 itu disarankan untuk aktif untuk semua koneksi:

Anda harus selalu mengatur ARITHABORT ke ON di sesi masuk Anda. Mengatur ARITHABORT ke OFF dapat berdampak negatif pada pengoptimalan kueri yang mengarah ke masalah kinerja.

Jadi ini sepertinya akan menjadi solusi ideal.

Pilihan 2

Jika opsi 1 tidak dapat dijalankan dan Anda menggunakan prosedur tersimpan untuk sebagian besar panggilan SQL Anda (yang seharusnya Anda lihat Prosedur Tersimpan vs. SQL Inline ) maka cukup aktifkan opsi di setiap prosedur tersimpan yang relevan:

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

Saya percaya solusi terbaik nyata di sini adalah dengan mengedit kode Anda, karena itu salah dan perbaikan lainnya hanyalah solusi.

6
LowlyDBA

Saya BUKAN ahli di sini tetapi Anda dapat mencoba sesuatu seperti di bawah ini.

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/

4
Sankar Reddy

Tidak ada pengaturan untuk memaksa SqlClient untuk selalu mengaktifkan ARITHABORT, Anda harus mengatur ini seperti yang Anda jelaskan.

Menariknya dari Dokumentasi Microsoft untuk SET ARITHABORT : -

Anda harus selalu mengatur ARITHABORT ke ON di sesi masuk Anda. Mengatur ARITHABORT ke OFF dapat berdampak negatif pada pengoptimalan kueri yang mengarah ke masalah kinerja.

Namun koneksi .Net adalah hard-coded untuk menonaktifkannya secara default?

Sebagai poin lain, Anda harus sangat berhati-hati ketika mendiagnosis masalah kinerja dengan pengaturan ini. Pilihan set yang berbeda akan menghasilkan rencana kueri yang berbeda untuk kueri yang sama. Kode .Net Anda dapat mengalami masalah kinerja (SET ARITHABORT OFF) namun saat Anda menjalankan kueri TSQL yang sama di SSMS (SET ARITHABORT ON ON secara default), mungkin tidak masalah. Ini karena. Kueri rencana tidak akan digunakan kembali dan rencana baru dihasilkan. Ini berpotensi menghilangkan masalah sniffing parameter misalnya dan memberikan kinerja yang jauh lebih baik.

2
Andy Jones

Jika itu menghemat waktu, dalam kasus saya (Entity Framework Core 2.0.3, ASP.Net Core API, SQL Server 2008 R2):

  1. Tidak ada Interceptors di EF Core 2.0 (saya pikir mereka akan segera tersedia di 2.1)
  2. Baik mengubah pengaturan DB global atau mengatur user_options dapat diterima untuk saya (mereka LAKUKAN bekerja - saya menguji) tetapi saya tidak bisa mengambil risiko mempengaruhi aplikasi lain.

Permintaan ad-hoc dari EF Core, dengan SET ARITHABORT ON; di atas, TIDAK berfungsi.

Akhirnya, solusi yang berhasil bagi saya adalah: Menggabungkan prosedur tersimpan, disebut sebagai kueri mentah dengan opsi SET sebelum EXEC dipisahkan oleh tanda titik koma, seperti ini:

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

Membangun jawaban Solomon Rutzy , untuk 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)
        ...

Ini menggunakan System.Data.Common 's DbCommand bukannya SqlCommand, dan DbConnection bukan SqlConnection.

Jejak SQL Profiler mengonfirmasi, SET ARITHABORT ON dikirim ketika koneksi terbuka, sebelum perintah lain dijalankan dalam transaksi.

0
CapNCook