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

Thực hiện xử lý lỗi và giao dịch trong SQL Server

Giới thiệu

Bất kể chúng ta cố gắng thiết kế và phát triển ứng dụng như thế nào, lỗi sẽ luôn xảy ra. Có hai loại chung - lỗi cú pháp hoặc lỗi lôgic có thể là lỗi chương trình hoặc hậu quả của việc thiết kế cơ sở dữ liệu không chính xác. Nếu không, bạn có thể gặp lỗi do người dùng nhập sai.

T-SQL (ngôn ngữ lập trình SQL Server) cho phép xử lý cả hai loại lỗi. Bạn có thể gỡ lỗi ứng dụng và quyết định những gì bạn cần làm để tránh lỗi trong tương lai.

Hầu hết các ứng dụng yêu cầu bạn ghi lại lỗi, triển khai báo cáo lỗi thân thiện với người dùng và khi có thể, hãy xử lý lỗi và tiếp tục thực thi ứng dụng.

Người dùng xử lý lỗi ở cấp độ câu lệnh. Nó có nghĩa là khi bạn chạy một loạt các lệnh SQL và sự cố xảy ra trong câu lệnh cuối cùng, mọi thứ trước vấn đề đó sẽ được cam kết với cơ sở dữ liệu dưới dạng các giao dịch ngầm định. Đây có thể không phải là điều bạn mong muốn.

Cơ sở dữ liệu quan hệ được tối ưu hóa để thực thi câu lệnh hàng loạt. Do đó, bạn cần thực hiện một loạt câu lệnh như một đơn vị và không thực hiện được tất cả các câu lệnh nếu một câu lệnh không thành công. Bạn có thể thực hiện điều này bằng cách sử dụng các giao dịch. Bài viết này sẽ tập trung vào cả xử lý lỗi và giao dịch, vì các chủ đề này được kết nối chặt chẽ với nhau.

Xử lý lỗi SQL

Để mô phỏng các ngoại lệ, chúng ta cần tạo chúng theo cách có thể lặp lại. Hãy bắt đầu với ví dụ đơn giản nhất - phép chia cho 0:

SELECT 1/0

Đầu ra mô tả lỗi được đưa ra - Đã gặp lỗi chia cho 0 . Nhưng lỗi này không được xử lý, ghi nhật ký hoặc tùy chỉnh để tạo ra thông báo thân thiện với người dùng.

Xử lý ngoại lệ bắt đầu bằng cách đưa các câu lệnh bạn muốn thực thi vào khối BEGIN TRY… END TRY.

SQL Server xử lý (bắt) lỗi trong khối BEGIN CATCH… END CATCH, nơi bạn có thể nhập logic tùy chỉnh để ghi hoặc xử lý lỗi.

Câu lệnh BEGIN CATCH phải theo sau câu lệnh END TRY. Việc thực thi sau đó được chuyển từ khối TRY sang khối CATCH khi xảy ra lỗi đầu tiên.

Tại đây, bạn có thể quyết định cách xử lý các lỗi, cho dù bạn muốn ghi dữ liệu về các trường hợp ngoại lệ đã nêu ra hay tạo một thông báo thân thiện với người dùng.

SQL Server có các chức năng tích hợp có thể giúp bạn trích xuất chi tiết lỗi:

  • ERROR_NUMBER ():Trả về số lỗi SQL.
  • ERROR_SEVERITY ():Trả về mức độ nghiêm trọng cho biết loại sự cố gặp phải và mức độ của sự cố. Người dùng có thể xử lý các cấp độ 11 đến 16.
  • ERROR_STATE ():Trả về số trạng thái lỗi và cung cấp thêm chi tiết về ngoại lệ đã ném. Bạn sử dụng số lỗi để tìm kiếm trong cơ sở kiến ​​thức Microsoft để biết chi tiết lỗi cụ thể.
  • ERROR_PROCEDURE ():Trả về tên của thủ tục hoặc trình kích hoạt mà trong đó lỗi đã được phát sinh hoặc NULL nếu lỗi không xảy ra trong thủ tục hoặc trình kích hoạt.
  • ERROR_LINE ():Trả về số dòng xảy ra lỗi. Đó có thể là số dòng của quy trình hoặc trình kích hoạt hoặc số dòng trong lô.
  • ERROR_MESSAGE ():Trả về văn bản của thông báo lỗi.

Ví dụ sau đây cho thấy cách xử lý lỗi. Ví dụ đầu tiên chứa Phép chia cho không lỗi, trong khi câu lệnh thứ hai đúng.

BEGIN TRY
   PRINT 1/0  
   SELECT 'Correct text'
END TRY
BEGIN CATCH
   SELECT ERROR_NUMBER() AS ERR_NO
   ,      ERROR_SEVERITY() AS ERR_SEV
   ,      ERROR_STATE() AS ERR_STATE
   ,      ERROR_LINE() AS ERR_LINE
   ,      ERROR_MESSAGE() AS ERR_MESSAGE
END CATCH

Nếu câu lệnh thứ hai được thực thi mà không xử lý lỗi (CHỌN ‘Văn bản chính xác’), thì câu lệnh đó sẽ thành công.

Vì chúng tôi triển khai xử lý lỗi tùy chỉnh trong khối TRY-CATCH, việc thực thi chương trình được chuyển đến khối CATCH sau lỗi trong câu lệnh đầu tiên và câu lệnh thứ hai không bao giờ được thực thi.

Bằng cách này, bạn có thể sửa đổi văn bản được cung cấp cho người dùng và kiểm soát những gì xảy ra nếu lỗi diễn ra tốt hơn. Ví dụ:chúng tôi ghi lỗi vào bảng nhật ký để phân tích thêm.

Sử dụng giao dịch

Logic nghiệp vụ có thể xác định rằng việc chèn câu lệnh đầu tiên không thành công khi câu lệnh thứ hai không thành công hoặc bạn có thể cần lặp lại các thay đổi của câu lệnh đầu tiên khi câu lệnh thứ hai bị lỗi. Việc sử dụng các giao dịch cho phép bạn thực hiện một loạt các câu lệnh như một đơn vị thất bại hoặc thành công.

Ví dụ sau minh họa việc sử dụng các giao dịch.

Đầu tiên, chúng tôi tạo một bảng để kiểm tra dữ liệu được lưu trữ. Sau đó, chúng tôi sử dụng hai giao dịch bên trong khối TRY-CATCH để mô phỏng những điều xảy ra nếu một phần của giao dịch không thành công.

Chúng tôi sẽ sử dụng câu lệnh CATCH với câu lệnh XACT_STATE (). Hàm XACT_STATE () được sử dụng để kiểm tra xem giao dịch có còn tồn tại hay không. Trong trường hợp giao dịch tự động quay trở lại, GIAO DỊCH QUAY LẠI sẽ tạo ra một ngoại lệ mới.

Có một chiến lợi phẩm tại mã dưới đây:

-- CREATE TABLE TEST_TRAN(VALS INT)

BEGIN TRY
   BEGIN TRANSACTION
       INSERT INTO TEST_TRAN(VALS) VALUES(1);
   COMMIT TRANSACTION  

   BEGIN TRANSACTION
       INSERT INTO TEST_TRAN(VALS) VALUES(2);
       INSERT INTO TEST_TRAN(VALS) VALUES('A'); 
       INSERT INTO TEST_TRAN(VALS) VALUES(3);
   COMMIT TRANSACTION
END TRY
BEGIN CATCH  
   IF XACT_STATE() > 0 ROLLBACK TRANSACTION

   SELECT ERROR_NUMBER() AS ERR_NO
   ,      ERROR_SEVERITY() AS ERR_SEV
   ,      ERROR_STATE() AS ERR_STATE
   ,      ERROR_LINE() AS ERR_LINE
   ,      ERROR_MESSAGE() AS ERR_MESSAGE

END CATCH

SELECT * FROM TEST_TRAN

-- DROP TABLE TEST_TRAN

Hình ảnh hiển thị các giá trị trong bảng TEST_TRAN và thông báo lỗi:

Như bạn thấy, chỉ giá trị đầu tiên được cam kết. Trong giao dịch thứ hai, chúng tôi đã gặp lỗi chuyển đổi loại ở hàng thứ hai. Do đó, toàn bộ lô đã quay trở lại.

Bằng cách này, bạn có thể kiểm soát dữ liệu nào đi vào cơ sở dữ liệu và cách xử lý các lô.

Tạo thông báo lỗi tùy chỉnh trong SQL

Đôi khi, chúng tôi muốn tạo các thông báo lỗi tùy chỉnh. Thông thường, chúng dành cho các tình huống khi chúng ta biết rằng sự cố có thể xảy ra. Chúng tôi có thể tạo thông báo tùy chỉnh của riêng mình nói rằng đã xảy ra sự cố mà không cần hiển thị chi tiết kỹ thuật. Vì vậy, chúng tôi đang sử dụng từ khóa THROW.

BEGIN TRY
   IF ( SELECT COUNT(sys.all_objects) > 1 )
	THROW ‘More than one object is ALL_OBJECTS system table’
END TRY
BEGIN CATCH
   SELECT ERROR_NUMBER() AS ERR_NO
   ,      ERROR_SEVERITY() AS ERR_SEV
   ,      ERROR_STATE() AS ERR_STATE
   ,      ERROR_LINE() AS ERR_LINE
   ,      ERROR_MESSAGE() AS ERR_MESSAGE
END CATCH

Hoặc, chúng tôi muốn có một danh mục các thông báo lỗi tùy chỉnh để phân loại và thống nhất theo dõi và báo cáo lỗi. SQL Server cho phép chúng tôi xác định trước mã thông báo lỗi, mức độ nghiêm trọng và trạng thái.

Một thủ tục được lưu trữ có tên “sys.sp_addmessage” được sử dụng để thêm các thông báo lỗi tùy chỉnh. Chúng tôi có thể sử dụng nó để gọi thông báo lỗi ở nhiều nơi.

Chúng tôi có thể gọi RAISERROR và gửi số tin nhắn dưới dạng tham số thay vì mã hóa cứng các chi tiết lỗi giống nhau ở nhiều vị trí trong mã.

Bằng cách thực thi mã đã chọn từ bên dưới, chúng tôi đang thêm lỗi tùy chỉnh vào SQL Server, nâng nó lên rồi sử dụng sys.sp_dropmessage để bỏ thông báo lỗi được chỉ định do người dùng xác định:

exec sys.sp_addmessage @msgnum=55000, @severity = 11, 
                                          @msgtext = 'My custom error message'
GO

RAISERROR(55000,11,1)
GO

exec sys.sp_dropmessage @msgnum=55000
GO

Ngoài ra, chúng ta có thể xem tất cả các thông báo trong SQL Server bằng cách thực hiện biểu mẫu truy vấn bên dưới. Thông báo lỗi tùy chỉnh của chúng tôi hiển thị dưới dạng mục đầu tiên trong tập kết quả:

SELECT * FROM master.dbo.sysmessages

Tạo hệ thống ghi lại lỗi

Việc ghi lại lỗi luôn hữu ích để gỡ lỗi và xử lý sau này. Bạn cũng có thể đặt trình kích hoạt trên các bảng đã ghi này và thậm chí thiết lập tài khoản email và sáng tạo một chút trong cách thông báo cho mọi người khi xảy ra lỗi.

Để ghi lại lỗi, chúng tôi tạo một bảng có tên DBError_Log , có thể được sử dụng để lưu trữ dữ liệu chi tiết nhật ký:

CREATE TABLE DBError_Log
(
    DBError_Log_ID    INT IDENTITY(1, 1) PRIMARY KEY,
    UserName              VARCHAR(100),
    ErrorNumber    INT,
    ErrorState     INT,
    ErrorSeverity  INT,
    ErrorLine      INT,
    ErrorProcedure VARCHAR(MAX),
    ErrorMessage   VARCHAR(MAX),
    ErrorDateTime  DATETIME
);

Để mô phỏng cơ chế ghi nhật ký, chúng tôi đang tạo GenError thủ tục được lưu trữ tạo ra Phân chia bằng 0 và ghi lại lỗi vào DBError_Log bảng:

CREATE PROCEDURE dbo.GenError
AS
  BEGIN TRY
    SELECT 1/0
  END TRY
  BEGIN CATCH
    INSERT INTO dbo.DBError_Log
    VALUES
    (SUSER_SNAME(),
     ERROR_NUMBER(),
     ERROR_STATE(),
     ERROR_SEVERITY(),
     ERROR_LINE(),
     ERROR_PROCEDURE(),
     ERROR_MESSAGE(),
     GETDATE()
	);
  END CATCH
GO

EXEC dbo.GenError
SELECT * FROM  dbo.DBError_Log

DBError_Log bảng chứa tất cả thông tin chúng tôi cần để gỡ lỗi. Ngoài ra, nó cung cấp thông tin bổ sung về quy trình gây ra lỗi. Mặc dù đây có vẻ là một ví dụ nhỏ nhặt, nhưng bạn có thể mở rộng bảng này với các trường bổ sung hoặc sử dụng nó để điền vào bảng đó bằng các trường hợp ngoại lệ được tạo tùy chỉnh.

Kết luận

Nếu chúng ta muốn duy trì và gỡ lỗi các ứng dụng, ít nhất chúng ta muốn báo cáo rằng đã xảy ra sự cố và cũng ghi nhật ký sự cố. Khi chúng tôi có một ứng dụng cấp sản xuất được sử dụng bởi hàng triệu người dùng, việc xử lý lỗi nhất quán và có thể báo cáo là chìa khóa để gỡ lỗi các vấn đề trong thời gian chạy.

Mặc dù chúng tôi có thể ghi lỗi ban đầu vào nhật ký lỗi cơ sở dữ liệu, nhưng người dùng sẽ thấy một thông báo thân thiện hơn. Do đó, sẽ là một ý tưởng hay nếu bạn triển khai các thông báo lỗi tùy chỉnh được gửi đến các ứng dụng gọi điện.

Bất kỳ thiết kế nào bạn triển khai, bạn cần ghi nhật ký và xử lý các ngoại lệ của người dùng và hệ thống. Nhiệm vụ này không khó với SQL Server, nhưng bạn cần lên kế hoạch ngay từ đầu.

Việc thêm các thao tác xử lý lỗi trên cơ sở dữ liệu đang chạy trong quá trình sản xuất có thể liên quan đến việc tái cấu trúc mã nghiêm trọng và các vấn đề hiệu suất khó tìm.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. nối cột dữ liệu được phân tách bằng dấu phẩy

  2. Sử dụng NEWSEQUENTIALID () để tạo HƯỚNG DẪN Tăng dần trong SQL Server

  3. Khắc phục Msg 241 “Chuyển đổi không thành công khi chuyển đổi ngày và / hoặc thời gian từ chuỗi ký tự” trong SQL Server

  4. Quyền THỰC HIỆN đã bị từ chối trên đối tượng 'xxxxxxx', cơ sở dữ liệu 'zzzzzzz', giản đồ 'dbo'

  5. Node.js MSSQL tedius ConnectionError:Không kết nối được với localhost:1433 - kết nối ECONNREFUSED