SQL Server - transaksi dibatalkan karena kesalahan?

193

Kami memiliki aplikasi klien yang menjalankan beberapa SQL pada SQL Server 2005 seperti berikut:

BEGIN TRAN;
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
COMMIT TRAN;

Ini dikirim oleh satu perintah string panjang.

Jika salah satu sisipan gagal, atau ada bagian dari perintah gagal, apakah SQL Server mengembalikan transaksi? Jika tidak mengembalikan, apakah saya harus mengirim perintah kedua untuk mengembalikannya?

Saya dapat memberikan spesifik tentang api dan bahasa yang saya gunakan, tetapi saya akan berpikir SQL Server harus menanggapi hal yang sama untuk bahasa apa pun.

jonathanpeppers
sumber

Jawaban:

205

Anda dapat menempatkan set xact_abort onsebelum transaksi Anda untuk memastikan sql memutar kembali secara otomatis jika terjadi kesalahan.

Greg B
sumber
1
Apakah ini akan bekerja pada MS SQL 2K dan lebih tinggi? Ini sepertinya solusi paling sederhana.
jonathanpeppers
1
Itu muncul di dokumen untuk 2000, 2005, dan 2008 jadi saya anggap ya. Kami menggunakannya pada 2008.
8
Apakah saya perlu mematikannya atau per sesi?
Marc
5
@ Mark lingkup xact_abortadalah pada tingkat koneksi.
Keith
2
@AlexMcMillan Pernyataan PROSEDUR DROP memodifikasi struktur basis data, tidak seperti INSERT, yang hanya berfungsi dengan data. Jadi tidak bisa dibungkus dalam transaksi. Saya terlalu menyederhanakan, tapi pada dasarnya begitulah adanya.
eksortso
195

Anda benar karena seluruh transaksi akan dibatalkan. Anda harus mengeluarkan perintah untuk mengembalikannya.

Anda dapat membungkus ini dalam satu TRY CATCHblok sebagai berikut

BEGIN TRY
    BEGIN TRANSACTION

        INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
        INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
        INSERT INTO myTable (myColumns ...) VALUES (myValues ...);

    COMMIT TRAN -- Transaction Success!
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK TRAN --RollBack in case of Error

    -- you can Raise ERROR with RAISEERROR() Statement including the details of the exception
    RAISERROR(ERROR_MESSAGE(), ERROR_SEVERITY(), 1)
END CATCH
Raj More
sumber
2
Saya lebih suka solusi DyingCactus, itu adalah 1 baris kode untuk diubah. Jika milik Anda jika karena alasan tertentu lebih baik (atau lebih dapat diandalkan) beri tahu saya.
jonathanpeppers
14
Coba tangkap memberi Anda kemampuan untuk menangkap (dan mungkin memperbaiki) kesalahan dan meningkatkan pesan kesalahan khusus jika diperlukan.
Raj More
11
"Tangkap dan masuk" lebih sering daripada "tangkap dan perbaiki", saya pikir.
quillbreaker
24
Sintaks RAISERROR salah setidaknya di SQL Server 2008R2 dan yang lebih baru. Lihat msdn.microsoft.com/en-us/library/ms178592.aspx untuk sintaks yang benar.
Eric J.
2
@BornToCode Untuk memastikan transaksi ada .. Katakanlah Anda telah memutar kembali transaksi Anda dalam kondisi tertentu (dalam try), tetapi kode gagal setelahnya. Tidak ada lagi transaksi, tetapi Anda masih akan masuk ke catch.
Gabriel GM
42

Di sini kode dengan mendapatkan pesan kesalahan berfungsi dengan MSSQL Server 2016:

BEGIN TRY
    BEGIN TRANSACTION 
        -- Do your stuff that might fail here
    COMMIT
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK TRAN

        DECLARE @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE()
        DECLARE @ErrorSeverity INT = ERROR_SEVERITY()
        DECLARE @ErrorState INT = ERROR_STATE()

    -- Use RAISERROR inside the CATCH block to return error  
    -- information about the original error that caused  
    -- execution to jump to the CATCH block.  
    RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);
END CATCH
samwise
sumber
1
Saya harus menggunakan DECLARE @Var TYPE; SET @Var = ERROR;untuk meningkatkan kesalahan dalam sql server 2005. Kalau tidak, kode di atas untuk meningkatkan kesalahan juga berfungsi untuk DB yang lebih lama. Mencoba untuk menetapkan nilai default ke variabel lokal adalah apa yang menyebabkan masalah.
jtlindsey
Anda dapat menggunakan THROW sederhana; bukannya deklarasi RAISERROR dan ERROR_ *.
rodzmkii
21

Dari artikel MDSN, Mengontrol Transaksi (Database Engine) .

Jika kesalahan pernyataan run-time (seperti pelanggaran kendala) terjadi dalam batch, perilaku default di Mesin Database adalah untuk memutar kembali hanya pernyataan yang menghasilkan kesalahan. Anda dapat mengubah perilaku ini menggunakan pernyataan SET XACT_ABORT. Setelah SET XACT_ABORT ON dijalankan, kesalahan pernyataan run-time apa pun menyebabkan kemunduran otomatis dari transaksi saat ini. Kompilasi kesalahan, seperti kesalahan sintaksis, tidak terpengaruh oleh SET XACT_ABORT. Untuk informasi lebih lanjut, lihat SET XACT_ABORT (Transact-SQL).

Dalam kasus Anda, itu akan mengembalikan transaksi lengkap ketika salah satu sisipan gagal.

Vitaly
sumber
3
apa yang kita butuhkan untuk menangani kesalahan sintaksis? atau kompilasi kesalahan? jika ada di antara mereka yang terjadi, seluruh transaksi harus dibatalkan
MonsterMMORPG
Menangkap kesalahan kompilasi / sintaks adalah untuk apa proyek SSDT. :-)
Joe the Coder
10

Jika salah satu sisipan gagal, atau ada bagian dari perintah gagal, apakah SQL server memutar kembali transaksi?

Tidak.

Jika tidak mengembalikan, apakah saya harus mengirim perintah kedua untuk mengembalikannya?

Tentu, Anda harus mengeluarkan ROLLBACKbukan COMMIT.

Jika Anda ingin memutuskan apakah akan melakukan atau mengembalikan transaksi, Anda harus menghapus COMMITkalimat dari pernyataan, periksa hasil dari sisipan dan kemudian menerbitkan salah satu COMMITatau ROLLBACKtergantung pada hasil cek.

Quassnoi
sumber
Jadi jika saya mendapatkan kesalahan, katakan "Konflik kunci primer" saya perlu mengirim panggilan kedua untuk mengembalikan? Saya rasa itu masuk akal. Apa yang terjadi jika ada kesalahan terkait jaringan seperti koneksi terputus selama pernyataan SQL berjalan sangat lama?
jonathanpeppers
2
Ketika koneksi terputus, protokol jaringan yang mendasarinya (misalnya Named Pipesatau TCP) memutus koneksi. Ketika koneksi terputus, SQL Serverhentikan semua perintah yang sedang berjalan dan kembalikan transaksi.
Quassnoi
1
Jadi solusi DyingCactus sepertinya memperbaiki masalah saya, terima kasih atas bantuannya.
jonathanpeppers
Jika Anda perlu untuk membatalkan pada setiap kesalahan, maka ya, ini merupakan pilihan terbaik.
Quassnoi