Existe uma maneira de passar informações para o gatilho Excluir, de modo que ele possa saber quem excluiu o registro?
Sim: usando um recurso muito interessante (e subutilizado) chamado CONTEXT_INFO
. É essencialmente a memória da sessão que existe em todos os escopos e não é vinculada por transações. Ele pode ser usado para transmitir informações (qualquer informação - bem, qualquer que se encaixe no espaço limitado) para acionadores, bem como para frente e para trás entre chamadas subprocessos / EXEC. E eu o usei antes para exatamente essa mesma situação.
Teste com o seguinte para ver como funciona. Observe que estou convertendo para CHAR(128)
antes do CONVERT(VARBINARY(128), ..
. Isso é forçar preenchimento em branco para facilitar a conversão VARCHAR
quando retirá-lo, CONTEXT_INFO()
pois VARBINARY(128)
é preenchido com 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];
Resultados:
0x492064656C6574656420414C4C20796F7572207265636F7264732120484120484121202020202020...
I deleted ALL your records! HA HA!
COLOCANDO TUDO JUNTO:
O aplicativo deve chamar um procedimento armazenado "Excluir" que transmita o nome de usuário (ou o que for) que está excluindo o registro. Presumo que este já seja o modelo que está sendo usado, pois parece que você já está acompanhando as operações de Inserir e Atualizar.
O procedimento armazenado "Excluir" faz:
DECLARE @EncodedUser VARBINARY(128);
SET @EncodedUser = CONVERT(VARBINARY(128),
CONVERT(CHAR(128), @UserName)
);
SET CONTEXT_INFO @EncodedUser;
-- DELETE STUFF HERE
O gatilho de auditoria faz:
-- 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;
Observe que, como o @SeanGallardy apontou em um comentário, devido a outros procedimentos e / ou consultas ad hoc para excluir registros desta tabela, é possível que:
CONTEXT_INFO
não foi definido e ainda é NULL
:
Por esse motivo, atualizei o acima INSERT INTO AuditTable
para usar a COALESCE
para padronizar o valor. Ou, se você não quiser um padrão e exigir um nome, poderá fazer algo semelhante a:
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
foi definido como um valor que não é um UserName válido e, portanto, pode exceder o tamanho do AuditTable.[UserWhoMadeChanges]
campo:
Por esse motivo, adicionei uma LEFT
função para garantir que o que for retirado CONTEXT_INFO
não interrompa o INSERT
. Conforme observado no código, você só precisa definir o 50
tamanho real do UserWhoMadeChanges
campo.
ATUALIZAÇÃO PARA SQL SERVER 2016 E MAIS NOVO
O SQL Server 2016 adicionou uma versão aprimorada dessa memória por sessão: Contexto da Sessão. O novo Contexto da Sessão é essencialmente uma tabela de hash de pares Valor-Chave, sendo o "Chave" do tipo sysname
(ie NVARCHAR(128)
) e o "Valor" SQL_VARIANT
. Significado:
- Agora existe uma separação de valores com menor probabilidade de entrar em conflito com outros usos
- Você pode armazenar vários tipos, sem precisar se preocupar com o comportamento estranho ao recuperar o valor via
CONTEXT_INFO()
(para obter detalhes, consulte minha postagem: Por que CONTEXT_INFO () não retorna o valor exato definido por SET CONTEXT_INFO? )
- Você obtém muito mais espaço: 8000 bytes no máximo por "Valor", até 256kb no total em todas as chaves (em comparação com o máximo de 128 bytes
CONTEXT_INFO
)
Para detalhes, consulte as seguintes páginas de documentação:
SUSER_SNAME()
é a chave para obter quem excluiu o registro.