Transaksi dan Coba-tangkap dalam Pekerjaan SQL Server

9

Kami memiliki operasi DML di setiap langkah pekerjaan SQL Server. Untuk memastikan update / insert akan digulung kembali jika sesuatu berjalan salah, saya telah membungkus modifikasi data setiap langkah dalam TRY CATCHdan TRANSACTIONblok:

BEGIN TRY
    BEGIN TRANSACTION

        [[INSERT/update statements]] ...

    IF @@TRANCOUNT > 0
    BEGIN
        COMMIT TRANSACTION
        PRINT 'Successful.'
    END

END TRY

BEGIN CATCH
    SELECT
        ERROR_NUMBER() AS ErrorNumber,
        ERROR_SEVERITY() AS ErrorSeverity,
        ERROR_STATE() AS ErrorState,
        ERROR_PROCEDURE() AS ErrorProcedure,
        ERROR_LINE() AS ErrorLine,
        ERROR_MESSAGE() AS ErrorMessage

    IF @@TRANCOUNT > 0
    BEGIN
        ROLLBACK TRANSACTION
        PRINT 'Unsuccessful.'
    END
END CATCH

Apakah itu memastikan manipulasi data akan dibatalkan jika terjadi kesalahan? Atau pertimbangan lain harus diperhitungkan?

Apakah ada cara yang lebih baik untuk melakukan itu (menggunakan konfigurasi, dll)?

Terima kasih.

Langit
sumber

Jawaban:

7

Saya lebih suka merekomendasikan pola seperti yang dari Exception Handling dan Nested Transactions :

create procedure [usp_my_procedure_name]
as
begin
    set nocount on;
    declare @trancount int;
    set @trancount = @@trancount;
    begin try
        if @trancount = 0
            begin transaction
        else
            save transaction usp_my_procedure_name;

        -- Do the actual work here

lbexit:
        if @trancount = 0   
            commit;
    end try
    begin catch
        declare @error int, @message varchar(4000), @xstate int;
        select @error = ERROR_NUMBER(), @message = ERROR_MESSAGE(), @xstate = XACT_STATE();
        if @xstate = -1
            rollback;
        if @xstate = 1 and @trancount = 0
            rollback
        if @xstate = 1 and @trancount > 0
            rollback transaction usp_my_procedure_name;

        raiserror ('usp_my_procedure_name: %d: %s', 16, 1, @error, @message) ;
    end catch   
end

Pola ini memeriksa XACT_STATE()dalam blok penahan untuk menjaga dari transaksi yang tidak dapat dilakukan :

Transaksi yang Tidak Terjanjikan dan XACT_STATE
Jika kesalahan yang dihasilkan dalam blok TRY menyebabkan keadaan transaksi saat ini menjadi tidak valid, transaksi tersebut diklasifikasikan sebagai transaksi yang tidak dapat dipastikan. Kesalahan yang biasanya mengakhiri transaksi di luar blok TRY menyebabkan transaksi memasuki kondisi yang tidak dapat dikomit ketika kesalahan terjadi di dalam blok TRY. Transaksi yang tidak dapat dilakukan hanya dapat melakukan operasi baca atau TRANSAKSI ROLLBACK. Transaksi tidak dapat menjalankan pernyataan Transact-SQL apa pun yang akan menghasilkan operasi penulisan atau TRANSAKSI KOMIT. Fungsi XACT_STATE mengembalikan nilai -1 jika transaksi telah diklasifikasikan sebagai transaksi yang tidak dapat dilakukan. Ketika batch selesai, Mesin Database memutar kembali setiap transaksi aktif yang tidak dapat dilakukan. Jika tidak ada pesan kesalahan yang dikirim saat transaksi memasuki kondisi yang tidak dapat diterima, ketika batch selesai, pesan kesalahan akan dikirim ke aplikasi klien. Ini menunjukkan bahwa transaksi yang tidak dapat dideteksi terdeteksi dan dibatalkan.

Kode Anda memeriksa @@TRANCOUNTdi tempat-tempat di mana itu tidak dapat 0, ia menggunakan campuran pesan PRINT informasi dan set hasil SELECT untuk berkomunikasi keberhasilan, itu tidak menangani kesalahan yang dapat dipulihkan. Idealnya pengecualian harus menyebar ke klien, dalam hal ini untuk pekerjaan Agen (mis. Tangkapan Anda harus naik kembali).

Remus Rusanu
sumber
Terima kasih atas jawaban Anda yang bermanfaat dan situs web yang fantastis! Namun saya bertanya-tanya apakah masih saya dapat menggunakan pola ini dengan pernyataan DML sederhana (bukan proc tersimpan)? Juga apakah kita harus menyimpan transaksi seperti di bawah ini? (Saya tidak punya proc toko untuk digunakan): simpan transaksi usp_my_procedure_name;
Langit
2

Apa yang Anda miliki terlihat bagus untuk saya. Saya akan menyarankan melakukan sesuatu dengan informasi tentu saja setelah Anda memutar kembali transaksi misalnya menuliskannya ke log.

datagod
sumber
1
Terima kasih atas balasan Anda, bisakah Anda memberi saya petunjuk bagaimana cara menulisnya ke log?
Langit
3
Jika Anda ingin menulis kesalahan atau data ke tabel log, maka sebelum Anda melakukan rollback, salin data yang Anda inginkan ke variabel tabel (Sangat penting bahwa Anda menggunakan variabel tabel, tabel temp akan digulirkan kembali.) Kemudian lakukan rollback, lalu masukkan data dari variabel tabel ke tabel logging.
HLGEM