Чи є спосіб передати інформацію на тригер видалення, щоб він міг знати, хто видалив запис?
Так: за допомогою дуже класного (і за умови використання функції), що називається CONTEXT_INFO
. По суті, це пам'ять сеансу, яка існує у всіх областях і не пов'язана транзакціями. Він може бути використаний для передачі інформації (будь-яка інформація - ну та будь-яка, яка вписується у обмежений простір) до тригерів, а також назад і назад між викликами sub-proc / EXEC. І я раніше його використовував для такої самої ситуації.
Перевірте наступне, щоб побачити, як це працює. Зверніть увагу , що я конвертування в CHAR(128)
перед CONVERT(VARBINARY(128), ..
. Це потрібно змусити пусту накладку, щоб полегшити її перетворення під VARCHAR
час виходу з неї, CONTEXT_INFO()
оскільки VARBINARY(128)
це правильно набито 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];
Результати:
0x492064656C6574656420414C4C20796F7572207265636F7264732120484120484121202020202020...
I deleted ALL your records! HA HA!
ВСТАВЛЯЄТЬСЯ ВСІМ РАЗОМ:
Додаток повинен викликати збережену процедуру "Видалити", яка передається в ім'я користувача (або будь-яке інше), що видаляє запис. Я припускаю, що це вже використовувана модель, оскільки це здається, що ви вже відстежуєте операції "Вставка та оновлення".
Збережена процедура "Видалити" робить:
DECLARE @EncodedUser VARBINARY(128);
SET @EncodedUser = CONVERT(VARBINARY(128),
CONVERT(CHAR(128), @UserName)
);
SET CONTEXT_INFO @EncodedUser;
-- DELETE STUFF HERE
Тригер аудиту робить:
-- 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;
Зауважте, що, як в коментарі вказував @SeanGallardy, через інші процедури та / або спеціальні запити про видалення записів із цієї таблиці можливо, що:
CONTEXT_INFO
не встановлено і все ще є NULL
:
З цієї причини я оновив вище, INSERT INTO AuditTable
щоб використовувати значення за COALESCE
замовчуванням. Або, якщо ви не хочете за замовчуванням і потребуєте імені, ви можете зробити щось подібне до:
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
було встановлено значення, яке не є дійсним ім'ям користувача, і, отже, може перевищувати розмір AuditTable.[UserWhoMadeChanges]
поля:
З цієї причини я додав LEFT
функцію, щоб гарантувати, що те, що схоплене, CONTEXT_INFO
не зламає INSERT
. Як зазначено в коді, вам просто потрібно встановити 50
фактичний розмір UserWhoMadeChanges
поля.
ОНОВЛЕННЯ ДЛЯ SQL SERVER 2016 ТА НОВІ
SQL Server 2016 додав вдосконалену версію цієї пам’яті за сеанс: Контекст сесії. Новий контекст сесії - це, по суті, хеш-таблиця пар Key-Value з буттям "Key" типу sysname
(тобто NVARCHAR(128)
) і "Value" SQL_VARIANT
. Значення:
- Зараз існує розділення значень, тому менше шансів на конфлікт з іншими напрямами
- Ви можете зберігати різні типи, більше не потрібно турбуватися про дивну поведінку при поверненні значення через
CONTEXT_INFO()
(детальніше, будь ласка, дивіться мій пост: Чому CONTEXT_INFO () не повертає точне значення, встановлене SET CONTEXT_INFO? )
- Ви отримуєте набагато більше місця: 8000 байт макс за "Значення", до 256 Кбіт усього для всіх клавіш (порівняно з 128 байт макс
CONTEXT_INFO
)
Докладніше див. На наступних сторінках документації:
SUSER_SNAME()
є ключем до того, хто отримав запис.