Kesalahan ini terjadi ketika Anda menggunakan blok coba/tangkap di dalam transaksi. Mari kita pertimbangkan contoh sepele:
SET XACT_ABORT ON
IF object_id('tempdb..#t') IS NOT NULL
DROP TABLE #t
CREATE TABLE #t (i INT NOT NULL PRIMARY KEY)
BEGIN TRAN
INSERT INTO #t (i) VALUES (1)
INSERT INTO #t (i) VALUES (2)
INSERT INTO #t (i) VALUES (3)
INSERT INTO #t (i) VALUES (1) -- dup key error, XACT_ABORT kills the batch
INSERT INTO #t (i) VALUES (4)
COMMIT TRAN
SELECT * FROM #t
Ketika penyisipan keempat menyebabkan kesalahan, kumpulan dihentikan dan transaksi dibatalkan. Tidak ada kejutan sejauh ini.
Sekarang mari kita coba menangani kesalahan itu dengan blok TRY/CATCH:
SET XACT_ABORT ON
IF object_id('tempdb..#t') IS NOT NULL
DROP TABLE #t
CREATE TABLE #t (i INT NOT NULL PRIMARY KEY)
BEGIN TRAN
INSERT INTO #t (i) VALUES (1)
INSERT INTO #t (i) VALUES (2)
BEGIN TRY
INSERT INTO #t (i) VALUES (3)
INSERT INTO #t (i) VALUES (1) -- dup key error
END TRY
BEGIN CATCH
SELECT ERROR_MESSAGE()
END CATCH
INSERT INTO #t (i) VALUES (4)
/* Error the Current Transaction cannot be committed and
cannot support operations that write to the log file. Roll back the transaction. */
COMMIT TRAN
SELECT * FROM #t
Kami menangkap kesalahan kunci duplikat, tetapi sebaliknya, kami tidak lebih baik. Batch kami masih dihentikan, dan transaksi kami masih dibatalkan. Alasannya sebenarnya sangat sederhana:
Pencekalan TRY/CATCH tidak memengaruhi transaksi.
Karena XACT_ABORT AKTIF, saat terjadi kesalahan kunci duplikat, transaksi akan gagal. Ini dilakukan untuk. Sudah terluka parah. Itu telah ditembakkan ke jantung... dan kesalahan yang harus disalahkan. TRY/CATCH memberikan SQL Server...nama yang buruk. (maaf, tidak bisa menolak)
Dengan kata lain, itu akan TIDAK PERNAH berkomitmen dan akan SELALU digulung kembali. Yang bisa dilakukan blok TRY/CATCH hanyalah mematahkan jatuhnya mayat. Kita dapat menggunakan XACT_STATE() berfungsi untuk melihat apakah transaksi kita dapat dikomit. Jika tidak, satu-satunya pilihan adalah membatalkan transaksi.
SET XACT_ABORT ON -- Try with it OFF as well.
IF object_id('tempdb..#t') IS NOT NULL
DROP TABLE #t
CREATE TABLE #t (i INT NOT NULL PRIMARY KEY)
BEGIN TRAN
INSERT INTO #t (i) VALUES (1)
INSERT INTO #t (i) VALUES (2)
SAVE TRANSACTION Save1
BEGIN TRY
INSERT INTO #t (i) VALUES (3)
INSERT INTO #t (i) VALUES (1) -- dup key error
END TRY
BEGIN CATCH
SELECT ERROR_MESSAGE()
IF XACT_STATE() = -1 -- Transaction is doomed, Rollback everything.
ROLLBACK TRAN
IF XACT_STATE() = 1 --Transaction is commitable, we can rollback to a save point
ROLLBACK TRAN Save1
END CATCH
INSERT INTO #t (i) VALUES (4)
IF @@TRANCOUNT > 0
COMMIT TRAN
SELECT * FROM #t
Pemicu selalu dieksekusi dalam konteks transaksi, jadi jika Anda dapat menghindari menggunakan TRY/CATCH di dalamnya, segalanya jauh lebih sederhana.
Untuk solusi masalah Anda, CLR Stored Proc dapat terhubung kembali ke SQL Server dalam koneksi terpisah untuk menjalankan SQL dinamis. Anda mendapatkan kemampuan untuk mengeksekusi kode dalam transaksi baru dan logika penanganan kesalahan mudah ditulis dan mudah dipahami dalam C#.