Có cách nào để truyền thông tin vào trình kích hoạt Xóa để nó có thể biết ai đã xóa bản ghi không?
Có: bằng cách sử dụng một tính năng rất hay (và được sử dụng) được gọi CONTEXT_INFO
. Nó thực chất là bộ nhớ phiên tồn tại trong tất cả các phạm vi và không bị ràng buộc bởi các giao dịch. Nó có thể được sử dụng để truyền thông tin (bất kỳ thông tin nào - tốt, bất kỳ thông tin nào phù hợp với không gian hạn chế) cho các trình kích hoạt cũng như qua lại giữa các lệnh gọi phụ / EXEC. Và tôi đã sử dụng nó trước đây cho tình huống chính xác này.
Kiểm tra với những điều sau đây để xem làm thế nào nó hoạt động. Lưu ý rằng tôi đang chuyển đổi CHAR(128)
trước CONVERT(VARBINARY(128), ..
. Điều này là để buộc đệm trống để dễ dàng chuyển đổi trở lại VARCHAR
khi lấy nó ra khỏi CONTEXT_INFO()
vì VARBINARY(128)
được đệm đúng với 0x00
s.
SELECT CONTEXT_INFO();
-- Initially = NULL
DECLARE @EncodedUser VARBINARY(128);
SET @EncodedUser = CONVERT(VARBINARY(128),
CONVERT(CHAR(128), 'I deleted ALL your records! HA HA!')
);
SET CONTEXT_INFO @EncodedUser;
SELECT CONTEXT_INFO() AS [RawContextInfo],
RTRIM(CONVERT(VARCHAR(128), CONTEXT_INFO())) AS [DecodedUser];
Các kết quả:
0x492064656C6574656420414C4C20796F7572207265636F7264732120484120484121202020202020...
I deleted ALL your records! HA HA!
ĐỂ TẤT CẢ CHÚNG CÙNG NHAU:
Ứng dụng sẽ gọi một thủ tục được lưu trữ "Xóa" đi qua Tên người dùng (hoặc bất cứ điều gì) đang xóa bản ghi. Tôi cho rằng đây đã là mô hình đang được sử dụng vì có vẻ như bạn đang theo dõi các hoạt động Chèn và Cập nhật.
Thủ tục lưu trữ "Xóa" không:
DECLARE @EncodedUser VARBINARY(128);
SET @EncodedUser = CONVERT(VARBINARY(128),
CONVERT(CHAR(128), @UserName)
);
SET CONTEXT_INFO @EncodedUser;
-- DELETE STUFF HERE
Trình kích hoạt kiểm toán thực hiện:
-- Set the INT value in LEFT (currently 50) to the max size of [UserWhoMadeChanges]
INSERT INTO AuditTable (IdOfRecordedAffected, UserWhoMadeChanges)
SELECT del.ID, COALESCE(
LEFT(RTRIM(CONVERT(VARCHAR(128), CONTEXT_INFO())), 50),
'<unknown>')
FROM DELETED del;
Xin lưu ý rằng, như @SeanGallardy đã chỉ ra trong một nhận xét, do các thủ tục khác và / hoặc truy vấn ad hoc xóa hồ sơ khỏi bảng này, có thể là:
CONTEXT_INFO
chưa được thiết lập và vẫn NULL
:
Vì lý do này, tôi đã cập nhật ở trên INSERT INTO AuditTable
để sử dụng một COALESCE
để mặc định giá trị. Hoặc, nếu bạn không muốn mặc định và yêu cầu một tên, thì bạn có thể làm điều gì đó tương tự:
DECLARE @UserName VARCHAR(50); -- set to the size of AuditTable.[UserWhoMadeChanges]
SET @UserName = LEFT(RTRIM(CONVERT(VARCHAR(128), CONTEXT_INFO())), 50);
IF (@UserName IS NULL)
BEGIN
ROLLBACK TRAN; -- cancel the DELETE operation
RAISERROR('Please set UserName via "SET CONTEXT_INFO.." and try again.', 16 ,1);
END;
-- use @UserName in the INSERT...SELECT
CONTEXT_INFO
đã được đặt thành một giá trị không phải là Tên người dùng hợp lệ và do đó có thể vượt quá kích thước của AuditTable.[UserWhoMadeChanges]
trường:
Vì lý do này, tôi đã thêm một LEFT
chức năng để đảm bảo rằng bất cứ thứ gì được lấy ra CONTEXT_INFO
sẽ không phá vỡ INSERT
. Như đã lưu ý trong mã, bạn chỉ cần đặt 50
kích thước thực của UserWhoMadeChanges
trường.
CẬP NHẬT CHO SQL SERVER 2016 VÀ MỚI hơn
SQL Server 2016 đã thêm phiên bản cải tiến của bộ nhớ mỗi phiên này: Ngữ cảnh phiên. Bối cảnh Phiên mới về cơ bản là một bảng băm của các cặp Khóa-Giá trị với "Khóa" là loại sysname
(nghĩa là NVARCHAR(128)
) và "Giá trị" SQL_VARIANT
. Ý nghĩa:
- Hiện tại có sự phân tách các giá trị nên ít có khả năng xung đột với các mục đích sử dụng khác
- Bạn có thể lưu trữ nhiều loại khác nhau, không còn phải lo lắng về hành vi kỳ quặc khi lấy lại giá trị thông qua
CONTEXT_INFO()
(để biết chi tiết, vui lòng xem bài đăng của tôi: Tại sao không phải TIẾP THEO BẮT ĐẦU () Trả về giá trị chính xác được đặt bởi SET CONTEXT_INFO? )
- Bạn nhận được nhiều dung lượng hơn: tối đa 8000 byte cho mỗi "Giá trị", tổng cộng tối đa 256kb trên tất cả các khóa (so với tối đa 128 byte
CONTEXT_INFO
)
Để biết chi tiết, xin vui lòng xem các trang tài liệu sau:
SUSER_SNAME()
là chìa khóa để có được những người đã xóa hồ sơ.