Apa metode terbaik untuk menambahkan penanganan kesalahan dalam procs disimpan SQL 2005?

11

Apa cara yang baik untuk membuat procs yang disimpan cukup kuat sehingga dapat menskala dengan sangat baik dan juga mengandung penanganan kesalahan?

Selain itu, apa cara terbaik untuk menangani beberapa skenario kesalahan dalam proc yang disimpan dan memiliki sistem umpan balik cerdas yang akan mengembalikan informasi kesalahan yang bermakna ke aplikasi panggilan?

kacalapy
sumber
2
Coba gunakan blok TRY CATCH yang lebih baru di SQL Server 2005. sommarskog.se/error_handling_2005.html
Sankar Reddy
Hai @ Pacalapy ~ Saya ingin merekomendasikan di masa depan untuk mengajukan setiap pertanyaan dengan caranya sendiri dan dengan begitu kita dapat memiliki jawaban spesifik yang difokuskan pada satu pertanyaan pada satu waktu. Saya mendorong Anda untuk melakukannya dengan pertanyaan ini.
jcolebrand

Jawaban:

12

Alex Kuznetsov memiliki bab hebat dalam bukunya Defensive Database Programming (Bab 8) yang mencakup T-SQL TRY ... CATCH, transaksi T-SQL & SET pengaturan XACT_ABORT, dan menggunakan penanganan kesalahan sisi klien. Ini akan banyak membantu Anda dalam memutuskan opsi mana yang paling masuk akal untuk apa yang perlu Anda capai.

Ini tersedia secara gratis di situs ini . Saya sama sekali tidak berafiliasi dengan perusahaan, tetapi saya memiliki versi cetak buku itu.

Ada banyak detail kecil tentang hal ini yang dijelaskan dengan sangat baik oleh Alex.

Per permintaan Nick ... (tetapi tidak semua ini ada di bab ini)

Dalam hal penskalaan, Anda harus jujur ​​secara brutal tentang aktivitas apa yang perlu dalam kode db dan yang harus ada dalam aplikasi. Pernah perhatikan bagaimana kode yang mengeksekusi cepat cenderung kembali merancang untuk satu masalah per metode?

Cara termudah untuk berkomunikasi adalah kode kesalahan khusus (> 50.000). Ini juga cukup cepat. Itu berarti Anda harus tetap menyinkronkan kode db dan kode aplikasi. Dengan kode kesalahan khusus, Anda juga dapat mengembalikan informasi yang berguna dalam string pesan kesalahan. Karena Anda memiliki kode kesalahan khusus untuk situasi itu, Anda bisa menulis parser dalam kode aplikasi yang disesuaikan dengan format data kesalahan itu.

Juga, kondisi kesalahan apa yang perlu coba lagi logika dalam database? Jika Anda ingin mencoba lagi setelah X detik, lebih baik Anda menangani itu dalam kode aplikasi sehingga transaksi tidak terlalu banyak diblokir. Jika Anda hanya mengirim kembali operasi DML segera, mengulanginya di SP bisa lebih efisien. Perlu diingat, bahwa Anda mungkin harus menduplikasi kode atau menambahkan lapisan SPs untuk mencapai coba lagi.

Sungguh, itu saat ini rasa sakit terbesar dengan MENCOBA ... CATCH logika di SQL Server saat ini. Itu bisa dilakukan, tapi itu sedikit omong kosong. Cari beberapa peningkatan yang terjadi pada SQL Server 2012 ini, terutama melemparkan kembali pengecualian sistem (mempertahankan nomor kesalahan asli). Juga, ada FORMATMESSAGE , yang menambahkan beberapa fleksibilitas dalam membangun pesan kesalahan, terutama untuk tujuan pencatatan.

Phil Helmer
sumber
Nasihat yang bagus dan buku yang sangat bagus!
Marian
Red Gate menawarkan beberapa E-book gratis yang sangat membantu, dan yang ini tentu saja salah satu yang lebih baik. Saran bagus.
Matt M
Tidak semua buku mereka melakukan ini, tetapi versi gratis buku "Defensive ..." Kuznetsov tidak mengandung 2 bab terakhir tentang Tingkat Isolasi Transaksi dan Modifikasi Berkembang yang bertahan dari konkurensi. Untuk saya. konten di sana layak dibeli.
Phil Helmer
7

Ini adalah templat kami (kesalahan logging dihapus)

Catatan:

  • Tanpa XACT_ABORT, semua TXN mulai dan komit / kembalikan harus dipasangkan
  • Komit mengurangi @@ TRANCOUNT
  • Rollback mengembalikan @@ TRANCOUNT ke nol sehingga Anda akan mendapatkan kesalahan 266
  • Anda tidak dapat ROLLBACK lapisan saat ini saja (mis. Penurunan @@ TRANCOUNT saat rollback)
  • XACT_ABORT menekan kesalahan 266
  • Setiap proc yang disimpan harus sesuai dengan templat yang sama sehingga setiap panggilan bersifat atomik
  • Pemeriksaan rollback sebenarnya berlebihan karena XACT_ABORT. Namun, itu membuat saya merasa lebih baik, terlihat aneh tanpa, dan memungkinkan untuk situasi di mana Anda tidak menginginkannya
  • Ini memungkinkan TXNs sisi klien (seperti LINQ)
  • Remus Rusanu memiliki shell serupa yang menggunakan save point. Saya lebih suka panggilan DB atom dan tidak menggunakan pembaruan parsial seperti artikel mereka

... jadi jangan membuat lebih banyak TXN daripada yang Anda butuhkan

Namun,

CREATE PROCEDURE [Name]
AS
SET XACT_ABORT, NOCOUNT ON

DECLARE @starttrancount int

BEGIN TRY
    SELECT @starttrancount = @@TRANCOUNT

    IF @starttrancount = 0
        BEGIN TRANSACTION

       [...Perform work, call nested procedures...]

    IF @starttrancount = 0 
        COMMIT TRANSACTION
END TRY
BEGIN CATCH
    IF XACT_STATE() <> 0 AND @starttrancount = 0 
        ROLLBACK TRANSACTION
    RAISERROR [rethrow caught error using @ErrorNumber, @ErrorMessage, etc]
END CATCH
GO
gbn
sumber
bagaimana jika @@ TRANCOUNT lebih besar dari 0? Anda tidak melakukan pekerjaan apa pun atau memiliki umpan balik?
kacalapy
@kacalapy: tidak ada yang namanya transaksi bersarang jadi kami tidak memulai scribd.com/doc/49579859/33/Nested-Transactions-Are-Real
gbn
3

Saya menggunakan Try / Catch, tetapi saya juga mengumpulkan informasi sebanyak mungkin dan menuliskannya ke errorlog SETELAH rollback. Dalam contoh ini, "LogEvent" adalah prosedur tersimpan yang menulis ke tabel EventLog, yang berisi perincian tentang apa yang terjadi. GetErrorInfo () adalah panggilan fungsi yang mengembalikan pesan kesalahan yang sebenarnya.

Ketika kesalahan terjadi, informasi dikumpulkan, prosedur melompat ke bagian penanganan kesalahan dan mengeluarkan rollback. Informasi tersebut ditulis ke log, kemudian prosedur keluar.

Mempertimbangkan prosedur tambahan / panggilan fungsi yang terlibat, sepertinya sedikit berlebihan. NAMUN metode ini sangat membantu ketika mencoba men-debug masalah.

exec LogEvent @Process, @Database, 'Mencoba memasukkan blah blah blah'
BEGIN COBALAH
  masukkan ke dalam MyTable
  pilih Nilai
    dari MyOtherTable

  pilih @rowcount = @@ ROWCOUNT
AKHIR MENCOBA
- Penanganan Kesalahan
BEGIN CATCH
  pilih @ error = ERROR_NUMBER (),
         @rowcount = -1,
         @TableAction = 'masukkan',
         @TableName = @Database + '.MyTable',
         @AdditionalInfo = '(Mencoba menyisipkan bla bla bla)' + dbo.GetErrorInfo ()
   GOTO TableAccessError
AKHIR CATCH

.
.
.
.

TableAccessError:
JIKA (@@ TRANCOUNT> 0) ROLLBACK
pilih @output = atas (@TableAction) + 
       'ERROR - Terjadi kesalahan saat' + 
       case (@TableAction)
         ketika 'memperbarui' lalu 'memperbarui'
         ketika 'hapus' lalu 'hapus'
         else @TableAction + 'ing'
       end + 
       'catatan' + 
       case (@TableAction) 
         ketika 'pilih' lalu 'dari' 
         ketika 'perbarui' lalu 'dalam' 
         ketika 'masukkan' lalu 'ke'
         lain dari   
         end + 
         tabel 'the' + @TableName + '.'
pilih @output = @output + '@@ ERROR:' + convert (varchar (8), @ error) 
pilih @output = @output + '@@ ROWCOUNT:' + convert (varchar (8), @ rowcount) 

pilih @output = @output + isnull (@AdditionalInfo, '')
exec LogEvent @Process, @Database, @Output
RAISERROR (@ output, 16,1) dengan log
pilih @ReturnCode = -1
GOTO THE_EXIT


datagod
sumber