Lỗi này xảy ra khi bạn sử dụng khối try / catch bên trong giao dịch. Hãy xem xét một ví dụ nhỏ:
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
Khi lần chèn thứ tư gây ra lỗi, lô sẽ bị chấm dứt và giao dịch quay trở lại. Không có bất ngờ cho đến nay.
Bây giờ, hãy cố gắng xử lý lỗi đó bằng khối 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
Chúng tôi đã gặp lỗi khóa trùng lặp, nhưng nếu không, chúng tôi không khá hơn. Lô của chúng tôi vẫn bị chấm dứt và giao dịch của chúng tôi vẫn được khôi phục. Lý do thực sự rất đơn giản:
Các khối TRY / CATCH không ảnh hưởng đến giao dịch.
Do việc BẬT XACT_ABORT, thời điểm xảy ra lỗi khóa trùng lặp, giao dịch sẽ bị hủy. Nó được thực hiện cho. Nó đã bị thương nặng. Nó bị bắn xuyên tim ... và có lỗi. TRY / CATCH mang lại cho SQL Server ... một cái tên tồi. (xin lỗi, không thể chống lại)
Nói cách khác, nó sẽ KHÔNG BAO GIỜ cam kết và sẽ LUÔN LUÔN được cuộn lại. Tất cả những gì khối TRY / CATCH có thể làm là phá vỡ sự rơi của xác chết. Chúng tôi có thể sử dụng XACT_STATE () chức năng để xem liệu giao dịch của chúng tôi có được cam kết hay không. Nếu không, lựa chọn duy nhất là quay trở lại giao dịch.
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
Các trình kích hoạt luôn thực hiện trong ngữ cảnh của một giao dịch, vì vậy nếu bạn có thể tránh sử dụng TRY / CATCH bên trong chúng, mọi thứ sẽ đơn giản hơn nhiều.
Để có giải pháp cho vấn đề của bạn, CLR Stored Proc có thể kết nối trở lại SQL Server trong một kết nối riêng biệt để thực thi SQL động. Bạn có khả năng thực thi mã trong một giao dịch mới và logic xử lý lỗi vừa dễ viết vừa dễ hiểu trong C #.