Ao configurar uma trilha de auditoria, não tenho problemas em rastrear quem está atualizando ou inserindo registros em uma tabela; no entanto, rastrear quem exclui registros parece mais problemático.
Eu posso rastrear Inserções / Atualizações incluindo no campo Inserir / Atualizar o "Atualizado por". Isso permite que o acionador INSERT / UPDATE tenha acesso ao campo "UpdatedBy" via inserted.UpdatedBy
. No entanto, com o gatilho Excluir, nenhum dado é inserido / atualizado. Existe uma maneira de passar informações para o gatilho Excluir, de modo que ele possa saber quem excluiu o registro?
Aqui está um gatilho Inserir / Atualizar
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
Usando o SQL Server 2012
sql-server
sql-server-2012
trigger
audit
webworm
fonte
fonte
SUSER_SNAME()
é a chave para obter quem excluiu o registro.SUSER_SNAME()
funcionaria em uma situação como um aplicativo da Web em que um único usuário pode ser usado para comunicação com o banco de dados de todo o aplicativo.Respostas:
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.As informações de contexto são VARBINÁRIAS (128)
Definido via: SET CONTEXT_INFO
Obter via: CONTEXT_INFO ()
Teste com o seguinte para ver como funciona. Observe que estou convertendo para
CHAR(128)
antes doCONVERT(VARBINARY(128), ..
. Isso é forçar preenchimento em branco para facilitar a conversãoVARCHAR
quando retirá-lo,CONTEXT_INFO()
poisVARBINARY(128)
é preenchido com0x00
s.Resultados:
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:
O gatilho de auditoria faz:
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 aCOALESCE
para padronizar o valor. Ou, se você não quiser um padrão e exigir um nome, poderá fazer algo semelhante a:CONTEXT_INFO
foi definido como um valor que não é um UserName válido e, portanto, pode exceder o tamanho doAuditTable.[UserWhoMadeChanges]
campo:Por esse motivo, adicionei uma
LEFT
função para garantir que o que for retiradoCONTEXT_INFO
não interrompa oINSERT
. Conforme observado no código, você só precisa definir o50
tamanho real doUserWhoMadeChanges
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
(ieNVARCHAR(128)
) e o "Valor"SQL_VARIANT
. Significado:CONTEXT_INFO()
(para obter detalhes, consulte minha postagem: Por que CONTEXT_INFO () não retorna o valor exato definido por SET CONTEXT_INFO? )CONTEXT_INFO
)Para detalhes, consulte as seguintes páginas de documentação:
fonte
@@SPID
. Esta é a memória PER-Session / Connection. Uma sessão não pode substituir as informações de contexto de outra sessão. E quando a sessão termina, o valor desaparece. Não existe um "item definido anteriormente".Você não pode fazer isso, a menos que esteja procurando gravar o ID do usuário do servidor SQL em vez de um nível de aplicativo.
Você pode fazer uma exclusão reversível tendo uma coluna chamada DeletedBy e definindo-a conforme necessário, para que seu gatilho de atualização faça a exclusão real (ou arquive o registro, eu geralmente evito exclusões físicas sempre que possível e legal), além de atualizar sua trilha de auditoria . Para forçar exclusões a serem feitas dessa maneira, defina um
on delete
gatilho que gera um erro. Se você não deseja adicionar uma coluna à sua tabela física, pode definir uma exibição que inclua a coluna e definainstead of
gatilhos para manipular a atualização da tabela base, mas isso pode ser um exagero.fonte
SPARSE
coluna do SQL Server ?Sim, aparentemente existem duas maneiras ;-). Se houver alguma reserva sobre o uso,
CONTEXT_INFO
como sugeri em minha outra resposta aqui , pensei em outra maneira que tenha uma separação funcional mais limpa de outros códigos / processos: use uma tabela temporária local.O nome da tabela temporária deve incluir o nome da tabela que está sendo excluída, pois isso ajudará a mantê-la separada de qualquer outro código que possa ser executado na mesma sessão. Algo ao longo das linhas de:
#<TableName>DeleteAudit
Um benefício para uma tabela temporária local
CONTEXT_INFO
é que, se alguém em outro processo - que é de alguma forma chamado desse processo "Excluir" em particular - apenas usa incorretamente o mesmo nome da tabela temporária, o subprocesso a) cria um novo local tabela temporária do nome solicitado que será separado dessa tabela temporária inicial (mesmo que tenha o mesmo nome) eb) quaisquer instruções DML em relação à nova tabela temporária local no subprocesso não afetarão nenhum dado no tabela temporária local criada aqui no processo pai, portanto, nenhuma substituição de dados. Claro que, se um problemas de subprocesso uma declaração DML contra este nome tabela temporária sem antes emitir um CREATE TABLE do mesmo nome, então essas instruções DML irá afetar os dados nesta tabela. MAS, neste momento estamos ficando realmenteaqui, ainda mais do que com a probabilidade de sobreposição de usosCONTEXT_INFO
(sim, eu sei que isso aconteceu, é por isso que digo "caso de aresta" em vez de "nunca acontecerá").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:
O gatilho de auditoria faz:
Eu testei esse código em um gatilho e funciona como esperado.
fonte