Есть ли способ передать информацию на триггер удаления, чтобы он мог знать, кто удалил запись?
Да: с помощью очень крутой (и недостаточно используемой функции) называется 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 добавил улучшенную версию памяти для каждого сеанса: контекст сеанса. Новый контекст сеанса, по сути, представляет собой хэш-таблицу пар ключ-значение с типом «ключ» sysname
(т.е. NVARCHAR(128)
) и значением «значение» SQL_VARIANT
. Смысл:
- В настоящее время существует разделение ценностей, поэтому вероятность конфликта с другими видами использования ниже.
- Вы можете хранить различные типы, больше не нужно беспокоиться о странном поведении при возврате значения через
CONTEXT_INFO()
(подробности см. В моем посте: Почему CONTEXT_INFO () не возвращает точное значение, установленное SET CONTEXT_INFO? )
- Вы получаете гораздо больше места: максимум 8000 байт на «Значение», до 256 КБ для всех ключей (по сравнению с максимумом 128 байт
CONTEXT_INFO
)
Для получения дополнительной информации, пожалуйста, смотрите следующие страницы документации:
SUSER_SNAME()
это ключ, чтобы узнать, кто удалил запись.