При настройке контрольного журнала у меня нет проблем с отслеживанием того, кто обновляет или вставляет записи в таблицу, однако отслеживание того, кто удаляет записи, кажется более проблематичным.
Я могу отслеживать вставки / обновления, включив в поле «Вставить / обновить» поле «Обновлено». Это позволяет триггеру INSERT / UPDATE иметь доступ к полю «UpdatedBy» через inserted.UpdatedBy
. Однако с помощью триггера Удалить данные не вставляются / обновляются. Есть ли способ передать информацию на триггер удаления, чтобы он мог знать, кто удалил запись?
Вот триггер вставки / обновления
ALTER TRIGGER [dbo].[trg_MyTable_InsertUpdate]
ON [dbo].[MyTable]
FOR INSERT, UPDATE
AS
INSERT INTO AuditTable (IdOfRecordedAffected, UserWhoMadeChanges)
VALUES (inserted.ID, inserted.LastUpdatedBy)
FROM inserted
Использование SQL Server 2012
sql-server
sql-server-2012
trigger
audit
мотылек
источник
источник
SUSER_SNAME()
это ключ, чтобы узнать, кто удалил запись.SUSER_SNAME()
будет работать в ситуации, как веб-приложение, где один пользователь может быть использован для связи с базой данных для всего приложения.Ответы:
Да: с помощью очень крутой (и недостаточно используемой функции) называется
CONTEXT_INFO
. По сути, это сессионная память, которая существует во всех областях и не связана транзакциями. Он может использоваться для передачи информации (любая информация - ну, любая, которая помещается в ограниченном пространстве) для триггеров, а также назад и вперед между вызовами sub-proc / EXEC. И я использовал это раньше для точно такой же ситуации.Контекстная информация VARBINARY (128)
Установить с помощью: SET CONTEXT_INFO
Получить через: CONTEXT_INFO ()
Проверьте следующее, чтобы увидеть, как это работает. Обратите внимание, что я обращаюсь
CHAR(128)
доCONVERT(VARBINARY(128), ..
. Это сделано для того, чтобы принудительно заполнить пробел, чтобы было легче преобразовать его обратноVARCHAR
при извлечении из него,CONTEXT_INFO()
такVARBINARY(128)
как он дополнен правой клавишей0x00
s.Результаты:
ВМЕСТЕ ВСЕ ВМЕСТЕ:
Приложение должно вызвать хранимую процедуру «Удалить», которая передает имя пользователя (или любое другое), которое удаляет запись. Я предполагаю, что это уже используемая модель, поскольку похоже, что вы уже отслеживаете операции вставки и обновления.
Хранимая процедура «Удалить» выполняет:
Триггер аудита выполняет:
Обратите внимание, что, как отметил @SeanGallardy в комментарии, из-за других процедур и / или специальных запросов, удаляющих записи из этой таблицы, возможно, что либо:
CONTEXT_INFO
не был установлен и все ещеNULL
:По этой причине я обновил выше,
INSERT INTO AuditTable
чтобы использовать значениеCOALESCE
по умолчанию. Или, если вы не хотите использовать значение по умолчанию и требовать имя, вы можете сделать что-то похожее на: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? )CONTEXT_INFO
)Для получения дополнительной информации, пожалуйста, смотрите следующие страницы документации:
источник
@@SPID
. Это память PER-Session / Connection. Один сеанс не может перезаписать контекстную информацию другого сеанса. И когда сеанс выходит из системы, значение исчезает. Не существует такого понятия, как «ранее установленный элемент».Это невозможно, если только вы не хотите записать идентификатор пользователя сервера SQL, а не один уровень приложения.
Вы можете сделать мягкое удаление, имея столбец с именем DeletedBy и устанавливая его по мере необходимости, тогда ваш триггер обновления может выполнять реальное удаление (или архивировать запись, я обычно избегаю жесткого удаления, где это возможно и разрешено законом), а также обновлять журнал аудита. , Чтобы принудительно выполнить удаление таким образом, определите
on delete
триггер, который вызывает ошибку. Если вы не хотите добавлять столбец в вашу физическую таблицу, вы можете определить представление, которое добавляет столбец и определитьinstead of
триггеры для обработки обновления базовой таблицы, но это может быть излишним.источник
SPARSE
столбца SQL Server ?Да, видимо, есть два пути ;-). Если есть какие-либо оговорки в отношении использования,
CONTEXT_INFO
как я предложил в моем другом ответе , я просто подумал о другом способе, который имеет более чистое функциональное отделение от другого кода / процессов: использовать локальную временную таблицу.Имя временной таблицы должно включать имя удаляемой таблицы, поскольку это поможет отделить его от любого другого кода, который может выполняться в том же сеансе. Что-то вроде:
#<TableName>DeleteAudit
Одно из преимуществ использования локальной временной таблицы
CONTEXT_INFO
заключается в том, что если кто-то в другом процессе, то есть как-то вызывать этот конкретный процесс «Удалить», просто неправильно использует одно и то же имя временной таблицы, подпроцесс а) создаст новый локальный временная таблица запрошенного имени, которая будет отделена от этой исходной временной таблицы (даже если она имеет то же имя), и б) любые операторы DML для новой локальной временной таблицы в подпроцессе не будут влиять на любые данные в Локальная временная таблица создана здесь в родительском процессе, следовательно, нет перезаписи данных. Конечно, если подпроцесс выдает оператор DML для этого имени временной таблицы, не выдав сначала CREATE TABLE с тем же именем, эти операторы DML будут влиять на данные в этой таблице. НО, на данный момент мы получаем действительнов данном случае, даже в большей степени, чем с вероятностью совпадения вариантов использованияCONTEXT_INFO
(да, я знаю, что это произошло, поэтому я говорю «крайний случай», а не «это никогда не произойдет»).Приложение должно вызвать хранимую процедуру «Удалить», которая передает имя пользователя (или любое другое), которое удаляет запись. Я предполагаю, что это уже используемая модель, поскольку похоже, что вы уже отслеживаете операции вставки и обновления.
Хранимая процедура «Удалить» выполняет:
Триггер аудита выполняет:
Я проверил этот код в триггере, и он работает, как ожидалось.
источник