Sqlserver
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Sqlserver

Cách xử lý lỗi trong giao dịch lồng nhau của máy chủ SQL

Trong bài viết này, chúng ta sẽ khám phá các Giao dịch lồng nhau của Máy chủ SQL, một khối giao dịch với một hoặc một số giao dịch.

Hình ảnh mô tả một mô hình đơn giản của giao dịch lồng nhau.

Giao dịch bên trong là một thủ tục được lưu trữ bao gồm các khối giao dịch. MSDN khuyến nghị “giữ các giao dịch càng ngắn càng tốt”, điều này hoàn toàn ngược lại với cách tiếp cận đầu tiên. Theo tôi, tôi không khuyến khích sử dụng các giao dịch lồng nhau. Tuy nhiên, đôi khi chúng tôi phải sử dụng chúng để giải quyết một số vấn đề kinh doanh.

Do đó, chúng ta sẽ tìm ra:

  • Điều gì sẽ xảy ra khi một giao dịch bên ngoài được khôi phục hoặc được cam kết?
  • Điều gì sẽ xảy ra khi một giao dịch nội bộ được hoàn nguyên hoặc được cam kết?
  • Cách xử lý các lỗi giao dịch lồng nhau?

Để bắt đầu, chúng tôi sẽ tạo một bảng demo và thử nghiệm các trường hợp có thể xảy ra.

USE AdventureWorks
-----Create Demo Table----
CREATE TABLE CodingSightDemo
(NumberValue VARCHAR(20))

Trường hợp 1:Cả giao dịch bên ngoài và bên trong đều được cam kết.

TRUNCATE TABLE CodingSightDemo  
--<*************OUTHER TRANSACTION START*************>
BEGIN TRAN				   
INSERT INTO CodingSightDemo	
VALUES('One')				
--<INNER TRANSACTION START>
BEGIN TRAN 					
INSERT INTO CodingSightDemo 		
VALUES('Two') 			
COMMIT TRAN	 			
--< INNER TRANSACTION END>
INSERT INTO CodingSightDemo 								VALUES('Three')				  
COMMIT TRAN		
--<************* OUTHER TRANSACTION END*************>
SELECT * FROM CodingSightDemo

Trong trường hợp này, tất cả các bản ghi được chèn thành công vào bảng. Chúng tôi đã giả định rằng mọi câu lệnh INSERT không trả về lỗi.

Trường hợp 2:Giao dịch bên ngoài được quay vòng trở lại , giao dịch nội bộ được cam kết .

TRUNCATE TABLE CodingSightDemo  
--<*************OUTHER TRANSACTION START*************>
BEGIN TRAN				   
INSERT INTO CodingSightDemo	
VALUES('One')				
--<INNER TRANSACTION START>
BEGIN TRAN 					
INSERT INTO CodingSightDemo 		
VALUES('Two') 			
COMMIT TRAN	 			
--< INNER TRANSACTION END>
INSERT INTO CodingSightDemo VALUES('Three')				  
rollback TRAN		
--<************* OUTHER TRANSACTION END*************>
SELECT * FROM CodingSightDemo

Như bạn có thể thấy, các bản ghi không được chèn vào bảng vì giao dịch bên trong là một phần của giao dịch bên ngoài. Vì lý do này, giao dịch bên trong sẽ quay trở lại.

Trường hợp 3:Giao dịch bên ngoài được cam kết , giao dịch bên trong được quay vòng trở lại .

TRUNCATE TABLE CodingSightDemo  
--<*************OUTHER TRANSACTION START*************>
BEGIN TRAN				   
INSERT INTO CodingSightDemo	
VALUES('One')				
--<INNER TRANSACTION START>
BEGIN TRAN 					
INSERT INTO CodingSightDemo 		
VALUES('Two') 			
ROLLBACK TRAN	 			
--< INNER TRANSACTION END>
INSERT INTO CodingSightDemo VALUES('Three')				  
COMMIT TRAN		
--<************* OUTHER TRANSACTION END*************>
SELECT * FROM CodingSightDemo

Trong trường hợp này, chúng tôi đã gặp lỗi và đã chèn câu lệnh mới nhất vào bảng. Do đó, một số câu hỏi nảy sinh:

  • Tại sao chúng tôi gặp lỗi?
  • Tại sao câu lệnh INSERT mới nhất được thêm vào bảng?

Theo quy tắc, câu lệnh ROLLBACK TRAN sẽ quay trở lại tất cả các giao dịch đang mở được thực hiện trong phiên hiện tại. Chúng tôi không thể viết một truy vấn vì nó sẽ trả về một lỗi.

BEGIN TRAN
INSERT INTO CodingSightDemo	
VALUES('One')	
BEGIN TRAN
INSERT INTO CodingSightDemo	
VALUES('Two')	
ROLLBACK TRAN
ROLLBACK TRAN

Chúng tôi sẽ xem xét quy tắc này có thể ảnh hưởng như thế nào đến trường hợp của chúng tôi. Câu lệnh ROLLBACK TRAN quay trở lại các giao dịch bên trong và bên ngoài. Vì lý do này, chúng tôi gặp lỗi khi chạy câu lệnh COMMIT TRAN vì không có giao dịch nào đang mở.

Tiếp theo, chúng tôi sẽ thêm một câu lệnh xử lý lỗi vào truy vấn này và sửa đổi nó dựa trên cách tiếp cận lập trình phòng thủ (như Wikipedia nói:Lập trình phòng thủ là một dạng thiết kế phòng thủ nhằm đảm bảo chức năng liên tục của một phần mềm trong những trường hợp không lường trước được). Khi chúng tôi viết một truy vấn mà không quan tâm đến việc xử lý lỗi và gặp lỗi, chúng tôi có thể phải đối mặt với việc hỏng tính toàn vẹn của dữ liệu.

Với tập lệnh tiếp theo, chúng tôi sẽ sử dụng điểm lưu. Chúng đánh dấu một điểm trong giao dịch và nếu muốn, bạn có thể khôi phục tất cả các câu lệnh DML (Ngôn ngữ thao tác dữ liệu) về điểm đã đánh dấu.

BEGIN TRY
BEGIN TRAN				   
INSERT INTO CodingSightDemo	
VALUES('One')				
--<INNER TRANSACTION START>
SAVE TRANSACTION innerTRAN
BEGIN TRY
BEGIN TRAN 					
INSERT INTO CodingSightDemo 		
VALUES('Two') 			
COMMIT TRAN
END TRY		
BEGIN CATCH
IF XACT_STATE() <> 0
BEGIN 
ROLLBACK TRANSACTION innerTRAN
PRINT 'Roll back occurs for inner tran'
END
IF XACT_STATE() <> 0
BEGIN 
COMMIT TRAN 
PRINT 'Commit occurs for firt open tran'
END
END CATCH
--< INNER TRANSACTION END>
INSERT INTO CodingSightDemo VALUES('Three')				  
COMMIT TRAN		
END TRY
BEGIN CATCH
BEGIN
IF XACT_STATE() <> 0
ROLLBACK TRAN 
PRINT 'Roll back occurs for outer tran'
END
END CATCH
--<************* OUTHER TRANSACTION END*************>
SELECT * FROM CodingSightDemo

Truy vấn này sẽ xử lý lỗi khi giao dịch bên trong bị lỗi. Ngoài ra, các giao dịch bên ngoài được cam kết thành công. Tuy nhiên, trong một số trường hợp, nếu giao dịch bên trong gặp lỗi, giao dịch bên ngoài phải khôi phục. Trong trường hợp này, chúng tôi sẽ sử dụng một biến cục bộ sẽ giữ và chuyển giá trị trạng thái lỗi truy vấn bên trong. Chúng tôi sẽ thiết kế truy vấn bên ngoài với giá trị biến này và truy vấn sẽ như sau.

--<*************OUTHER TRANSACTION START*************>
DECLARE @innertranerror as int=0
BEGIN TRY
BEGIN TRAN				   
INSERT INTO CodingSightDemo	
VALUES('One')				
--<INNER TRANSACTION START>
SAVE TRANSACTION innerTRAN
BEGIN TRY
BEGIN TRAN 					
INSERT INTO CodingSightDemo 		
VALUES('Two') 			
COMMIT TRAN
END TRY		
BEGIN CATCH
IF XACT_STATE() <> 0
BEGIN 
SET @innertranerror=1
ROLLBACK TRANSACTION innerTRAN
PRINT 'Roll back occurs for inner tran'
END
IF XACT_STATE() <> 0
BEGIN 
COMMIT TRAN 
PRINT 'Commit occurs for firt open tran'
END
END CATCH
--< INNER TRANSACTION END>
INSERT INTO CodingSightDemo VALUES('Three')	
if @innertranerror=0
BEGIN
COMMIT TRAN	
END
IF @innertranerror=1
BEGIN
ROLLBACK TRAN
END

END TRY
BEGIN CATCH
BEGIN
IF XACT_STATE() <> 0
ROLLBACK TRAN 
PRINT 'Roll back occurs for outer tran'
END
END CATCH
--<************* OUTHER TRANSACTION END*************>
SELECT * FROM CodingSightDemo

Kết luận

Trong bài viết này, chúng tôi đã khám phá các giao dịch lồng nhau và phân tích cách xử lý lỗi trong loại truy vấn này. Quy tắc quan trọng nhất về loại giao dịch này là viết các truy vấn phòng thủ vì chúng ta có thể gặp lỗi trong các giao dịch bên ngoài hoặc bên trong. Vì lý do này, chúng tôi phải thiết kế hành vi xử lý lỗi của truy vấn.

Tài liệu tham khảo

Giao dịch lồng ghép

LƯU GIAO DỊCH


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Sự khác biệt giữa datetime và timestamp trong sqlserver?

  2. Chèn nhiều hàng trong một truy vấn SQL?

  3. Nhận hàng cập nhật

  4. Cách bật Tất cả Ràng buộc CHECK &Khóa ngoại trong Cơ sở dữ liệu trong SQL Server (Ví dụ T-SQL)

  5. Stuff và 'For Xml Path' hoạt động như thế nào trong SQL Server?