Tìm hiểu về câu lệnh RAISERROR của SQL Server

Bài viết này sẽ giúp chúng ta có cái nhìn tổng quan về câu lệnh RAISERROR trong SQL Server. Ngoài ra chúng ta sẽ có một vài ví dụ để có thể áp dụng câu lệnh này vào code một cách hiệu quả nhất.

11 phút để đọc
Bởi Đức Trí
Tìm hiểu về câu lệnh RAISERROR của SQL Server
Ảnh chụp bởi Camylla Battani / Unsplash

Các bạn có thể xem tài liệu chính thức về câu lệnh RAISERROR của Microsoft tại đây.


Giới thiệu

Câu lệnh RAISERROR được dùng để tạo một nội dung (thường là lỗi, nhưng cũng có thể là nội dung khác như debug) để trả về cho ứng dụng. Nội dung này có thể được bắt bởi câu lệnh TRY...CATCH hoặc trả về trực tiếp cho ứng dụng đang kết nối vào SQL Server.

Nếu ứng dụng là SSMS thì nội dung này sẽ nằm trong tab Messages.

Cú pháp

RAISERROR ( { msg_id | msg_str | @local_variable }  
    { ,severity ,state }  
    [ ,argument [ ,...n ] ] )  
    [ WITH option [ ,...n ] ]  
Cú pháp câu lệnh RAISERROR (nguồn: Microsoft)

Như có thể thấy trong cú pháp này, RAISERROR nhận 1 trong 3 loại đầu vào cho tham số đầu tiên:

  • msg_id - là số định danh của mẫu câu có sẵn được lưu trong SQL Server
  • msg_str - là nội dung tự chọn mà bạn mong muốn được xuất ra bởi RAISERROR
  • @local_variable - là biến của ngôn ngữ T-SQL, có thể là số (tương ứng với msg_id) hoặc chuỗi (tương ứng với msg_str)

Sử dụng định danh của mẫu có sẵn trong SQL Server

Nếu tham số đầu tiên là số dưới dạng INT, SQL Server sẽ sử dụng con số này để lấy mẫu câu có sẵn được lưu trong bảng sys.messages để làm nội dung xuất ra cho RAISERROR.

Các bạn có thể đọc thêm tài liệu chính thức của Microsoft về bảng sys.messages tại đây.

Sử dụng mẫu của hệ thống

Mẫu có sẵn của hệ thống mà RAISERROR có thể dùng được có định danh nằm trong khoảng 13000 đến 49999. Cụ thể, các bạn có thể sử dụng câu lệnh ở dưới để liệt kê các mẫu có sẵn của hệ thống.

SELECT	*
FROM	sys.messages
WHERE	message_id BETWEEN 13000 AND 49999;
Truy vấn danh sách mẫu có sẵn của hệ thống
Truy vấn danh sách mẫu có sẵn của hệ thống

Ví dụ, nếu bạn muốn lấy mẫu có định danh 14138 với tham số cho mẫu là Aha, bạn có thể sử dụng câu lệnh sau:

RAISERROR(14138, -1, -1, N'Aha');
Sử dụng RAISERROR với mẫu có sẵn của hệ thống
Sử dụng RAISERROR với mẫu có sẵn của hệ thống

Sử dụng mẫu tự tạo

Ngoài mẫu có sẵn của hệ thống, SQL Server cũng cho phép người dùng tự tạo mẫu cho riêng mình bằng cách sử dụng SP (stored procedure) sys.sp_addmessage (xem thêm).

Mẫu tự tạo mới bắt buộc phải sử dụng định danh từ 50001 trở lên và không trùng định danh với mẫu đã tạo trước đó.

SELECT	*
FROM	sys.messages
WHERE	message_id > 50000;

EXECUTE	sys.sp_addmessage
			@msgnum		= 50001
		,	@severity	= 16
		,	@msgtext	= N'vndba.net test error message - %s'
		;

SELECT	*
FROM	sys.messages
WHERE	message_id > 50000;
Thêm mẫu tự tạo mới vào danh sách mẫu câu của SQL Server
Thêm mẫu tự tạo mới vào danh sách mẫu câu của SQL Server

Sau khi tạo mẫu thì chúng ta có thể sử dụng nó giống như là mẫu của hệ thống.

RAISERROR(50001, -1, -1, N'Aha');
Sử dụng RAISERROR với mẫu tự tạo
Sử dụng RAISERROR với mẫu tự tạo

Sử dụng nội dung tự chọn

Nếu tham số đầu tiên là chuỗi dưới dạng CHAR, NCHAR, VARCHAR hoặc NVARCHAR, SQL Server sẽ sử dụng chuỗi này để làm nội dung xuất ra cho RAISERROR.

RAISERROR chỉ hỗ trợ chuỗi với nội dung không quá 2047 ký tự. Nếu nội dung có số lượng ký tự từ 2048 trở lên, RAISERROR sẽ chỉ in ra 2044 ký tự và kết thúc bằng dấu ba chấm ...

Khi sử dụng cách này, RAISERROR sẽ sử dụng định danh 50000 cho nội dung trả về.

RAISERROR(N'Custom message from vndba.net - %s', 16, 1, N'Aha');
Sử dụng RAISERROR với nội dung tự chọn
Sử dụng RAISERROR với nội dung tự chọn

Sử dụng biến làm đầu vào

Nếu tham số đầu tiên là biến của T-SQL, thì tùy theo kiểu dữ liệu của biến là INT hay CHAR/NCHAR/VARCHAR/NVARCHARRAISERROR sẽ xử lý tương ứng như đã trình bày ở trên.

Ví dụ nếu biến là định danh của mẫu:

DECLARE	@input INT = 50001;
RAISERROR(@input, -1, -1, N'Aha');
Sử dụng RAISERROR với biến là định danh của mẫu
Sử dụng RAISERROR với biến là định danh của mẫu

Và ví dụ nếu biến là nội dung tự chọn:

DECLARE	@input NVARCHAR(MAX) = N'Custom message from vndba.net - %s';
RAISERROR(@input, 16, 1, N'Aha');
Sử dụng RAISERROR với biến là nội dung tự chọn
Sử dụng RAISERROR với biến là nội dung tự chọn

Độ nghiêm trọng severity và tình trạng state của thông báo lỗi

Mục tiêu của bài này là giới thiệu sơ bộ nên mình sẽ không đi sâu vào giải thích 2 thuộc tính này. Nếu có dịp mình sẽ trình bày trong một bài khác.

Tuy nhiên để có thể sử dụng RAISERROR theo ý muốn thì các bạn cũng nên có một cái nhìn sơ về 2 thuộc tính này.

Độ nghiêm trọng severity

Mỗi thông báo lỗi đều được gắn với một mức độ nghiêm trọng để SQL Server và ứng dụng có thể biết được để xử lý tương xứng. Cụ thể về severity các bạn có thể xem thêm tại đây.

Cơ bản thì mình quan tâm đến các giá trị sau của severity:

severity Cách sử dụng
0 | 10 Nội dung bình thường (không phải lỗi), và chỉ in nội dung.
Sử dụng cho nội dung thông báo bình thường (information).
1 ~ 9 Nội dung bình thường (không phải lỗi), và in đầy đủ thông tin.
Sử dụng cho nội dung debug.
16 Sử dụng cho lỗi tự tạo
-1 Sử dụng giá trị mặc định của mẫu

Khi các bạn sử dụng SSMS thì toàn bộ nội dung mà RAISERROR tạo ra đều nằm trong tab Messages. Nội dung bình thường sẽ có màu đen còn nội dung lỗi sẽ có màu đỏ.

Tình trạng state

Gọi là tình trạng vậy thôi, chứ trên thực tế giá trị này được tạo ra để có thể xác định vị trí của lỗi một cách dễ dàng hơn bằng cách sử dụng các giá trị state khác nhau ở những đoạn code khác nhau.

state nhận giá trị từ 0  đến 255, và mình cũng khuyến khích các bạn sử dụng giá trị trong khoảng này.

Ví dụ

RAISERROR(N'This message has severity [0] and state [0]', 0, 0);
PRINT '---';
RAISERROR(N'This message has severity [1] and state [1]', 1, 1);
PRINT '---';
RAISERROR(N'This message has severity [7] and state [7]', 7, 7);
PRINT '---';
RAISERROR(N'This message has severity [16] and state [1]', 16, 1);
PRINT '---';
RAISERROR(N'This message has severity [16] and state [255]', 16, 255);
PRINT '---';
Sử dụng RAISERROR với các giá trị severitystate khác nhau
Sử dụng RAISERROR với các giá trị severitystate khác nhau

Định nghĩa và sử dụng tham số nhúng

Ở một vài ví dụ ở trên, các bạn có thể thấy mình sử dụng tham số cho mẫu hoặc nội dung bằng cách truyền giá trị tham số vào vị trí thứ 4 trong câu lệnh. Giá trị này thay thế cờ %s trong nội dung được in ra.

Sử dụng tham số nhúng bên trong nội dung của RAISERROR

Bạn có thể định nghĩa nhiều hơn một cờ trong nội dung, khi đó RAISERROR sẽ sử dụng các tham số ở vị trí tiếp theo để thay thế cho các cờ này.

Cấu trúc hoàn chỉnh của cờ khá là phức tạp, nên mình để dành nội dung này cho một bài khác. Trong bài này mình chỉ liệt kê ra các cờ thông dụng mà mình hay sử dụng và kiểu dữ liệu tương ứng có thể sử dụng cho cờ đó.

Cờ Kiểu dữ liệu có thể dùng
%s CHAR NCHAR VARCHAR NVARCHAR
%d INT
%I64d BIGINT
%#x BINARY VARBINARY
%% In ra ký tự %

Điểm trừ lớn nhất của tham số nhúng là không hỗ trợ các kiểu dữ liệu thập phân như DECIMAL, FLOAT hay MONEY/SMALLMONEY. Tuy nhiên các bạn vẫn có thể dùng CAST hoặc CONVERT để chuyển dữ liệu thập phân về dạng chuỗi,  sau đó sử dụng cờ %s để làm tham số nhúng.

DECLARE	@s		NVARCHAR(MAX)	= N'Test string'
	,	@d		INT				= 12345
	,	@I64d	BIGINT			= 123456789012345
	,	@x		VARBINARY(MAX)	= 0x12345678
	,	@f		DECIMAL(16,3)	= 12345.678
	,	@fs		NVARCHAR(MAX)
	;
SELECT	@fs = CONVERT(NVARCHAR(MAX), @f);
RAISERROR(N'\
Flag %%s - %s
---
Flag %%d - %d
---
Flag %%I64d - %I64d
---
Flag %%#x - %#x
---
Flag %%s for decimal - %s
---', 0, 0, @s, @d, @I64d, @x, @fs);
Sử dụng tham số nhúng trong RAISERROR
Sử dụng tham số nhúng trong RAISERROR

Tùy chọn bổ sung WITH NOWAIT

RAISERROR cho phép người dùng bổ sung thêm tùy chọn để thay đổi cách SQL Server xử lý câu lệnh. Trong bài này, mình chỉ đề cập đến tùy chọn được sử dụng nhiều nhất, cũng như là lý do chính mà mình sử dụng RAISERROR, đó là tùy chọn WITH NOWAIT.

Hãy lấy ví dụ sau làm minh họa cho tình huống thực tế (ví dụ như theo dõi tiến độ chạy của script):

RAISERROR(N'First message', 0, 0);
PRINT '---';
RAISERROR(N'Second message', 0, 0);
PRINT '---';
WAITFOR DELAY '00:00:05';
RAISERROR(N'Final message after 5 seconds', 0, 0);
PRINT '---';
RAISERROR không có tùy chọn

Trong đoạn code trên, mình đang muốn in ra nội dung First messageSecond message, sau đó chờ 5 giây rồi mới in ra Final message after 5 seconds. Nhưng khi chạy đoạn code trên thì hoàn toàn không giống mong đợi:

RAISERROR không có tùy chọn

SSMS đợi hết 5 giây rồi mới in ra cả 3 nội dung. Sở dĩ có hành vi này là do SSMS sử dụng buffer cho nội dung trả về từ SQL Server. Cho đến khi truy vấn kết thúc hoặc buffer đầy (khoảng 8KB - xem thêm) thì SSMS mới in kết quả ra cho chúng ta. Điều này cũng áp dụng cho nội dung in ra từ câu lệnh PRINT.

May thay, RAISERROR cho phép chúng ta bật tùy chọn WITH NOWAIT để ngay lập tức in ra nội dung mà không cần phải đợi truy vấn kết thúc hay buffer đầy nữa. Hãy xem thử script của chúng ta có hành xử đúng như mong muốn sau khi thêm tùy chọn này hay không:

RAISERROR(N'First message', 0, 0) WITH NOWAIT;
PRINT '---';
RAISERROR(N'Second message', 0, 0) WITH NOWAIT;
PRINT '---';
WAITFOR DELAY '00:00:05';
RAISERROR(N'Final message after 5 seconds', 0, 0) WITH NOWAIT;
PRINT '---';
RAISERROR với tùy chọn WITH NOWAIT
RAISERROR với tùy chọn WITH NOWAIT

Tada. Ngay lập tức chúng ta nhận ra sự khác biệt. First messageSecond message đều được in ra như mong đợi. Ngoài ra chúng ta cũng có thể thấy --- của câu lệnh PRINT đầu tiên được in ra theo đúng thứ tự, nhưng --- tiếp theo lại không được in ra. Điều này là do như đã nói ở trên, PRINT phải đợi truy vấn kết thúc hoặc khi buffer được giải phóng do đầy hoặc RAISERROR ... WITH NOWAIT thì mới được in ra.

Chính vì cách hành xử của RAISERROR ... WITH NOWAIT giống với mong đợi của mình nên mình luôn sử dụng tùy chọn này mỗi khi dùng RAISERROR.

Lời kết

Trên đây là toàn bộ những kiến thức cơ bản về câu lệnh RAISERROR mà mình tổng hợp lại. Hy vọng bài này có thể giúp các bạn biết và hiểu thêm về câu lệnh này, cũng như có thể áp dụng được RAISERROR (hay nói cách khác, RAISERROR ... WITH NOWAIT) vào công việc của các bạn.

Nếu có vấn đề gì thắc mắc liên quan đến chủ đề này, các bạn có thể liên hệ mình thông qua Facebook Messenger. Chúc các bạn một ngày làm việc vui vẻ ./.

Vietnam DBA Community


Theo dõi