Al configurar un seguimiento de auditoría no tengo problemas para rastrear quién está actualizando o insertando registros en una tabla, sin embargo, rastrear quién elimina registros parece más problemático.
Puedo rastrear Inserciones / Actualizaciones incluyendo en Insertar / Actualizar el campo "Actualizado por". Esto permite que el activador INSERT / UPDATE tenga acceso al campo "UpdatedBy" a través de inserted.UpdatedBy
. Sin embargo, con el disparador Eliminar no se insertan / actualizan datos. ¿Hay alguna manera de pasar información al desencadenador Eliminar de modo que pueda saber quién eliminó el registro?
Aquí hay un disparador Insertar / Actualizar
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 SQL Server 2012
sql-server
sql-server-2012
trigger
audit
gusano web
fuente
fuente
SUSER_SNAME()
es la clave para obtener quién borró el registro.SUSER_SNAME()
que funcione en una situación como una aplicación web donde un solo usuario podría ser utilizado para la comunicación de la base de datos para toda la aplicación.Respuestas:
Sí: mediante el uso de una llamada muy interesante (y poco utilizada)
CONTEXT_INFO
. Es esencialmente la memoria de sesión que existe en todos los ámbitos y no está vinculada por las transacciones. Se puede usar para pasar información (cualquier información, bueno, cualquiera que se ajuste al espacio limitado) a disparadores, así como de ida y vuelta entre llamadas subproc / EXEC. Y lo he usado antes para esta misma situación.La información de contexto es VARBINARIA (128)
Establecer a través de: SET CONTEXT_INFO
Obtener a través de: CONTEXT_INFO ()
Pruebe con lo siguiente para ver cómo funciona. Tenga en cuenta que me estoy convirtiendo
CHAR(128)
antes deCONVERT(VARBINARY(128), ..
. Esto es para forzar el relleno en blanco para que sea más fácil volver a convertirlo alVARCHAR
sacarlo,CONTEXT_INFO()
ya queVARBINARY(128)
está rellenado con0x00
s.Resultados:
PONIENDOLO TODO JUNTO:
La aplicación debe llamar a un procedimiento almacenado "Eliminar" que pasa el nombre de usuario (o lo que sea) que está eliminando el registro. Supongo que este ya es el modelo que se está utilizando, ya que parece que ya está rastreando las operaciones de Insertar y Actualizar.
El procedimiento almacenado "Eliminar" hace:
El disparador de auditoría hace:
Tenga en cuenta que, como señaló @SeanGallardy en un comentario, debido a otros procedimientos y / o consultas ad hoc que eliminan registros de esta tabla, es posible que:
CONTEXT_INFO
no se ha configurado y sigue siendoNULL
:Por esta razón, he actualizado lo anterior
INSERT INTO AuditTable
para usar aCOALESCE
para predeterminar el valor. O, si no desea un valor predeterminado y requiere un nombre, entonces podría hacer algo similar a:CONTEXT_INFO
se ha establecido en un valor que no es un nombre de usuario válido y, por lo tanto, puede exceder el tamaño delAuditTable.[UserWhoMadeChanges]
campo:Por esta razón, agregué una
LEFT
función para garantizar que lo que sea que se extraigaCONTEXT_INFO
no rompa elINSERT
. Como se indica en el código, solo necesita establecer el50
tamaño real delUserWhoMadeChanges
campo.ACTUALIZACIÓN PARA SQL SERVER 2016 Y MÁS NUEVOS
SQL Server 2016 agregó una versión mejorada de esta memoria por sesión: Contexto de sesión. El nuevo contexto de sesión es esencialmente una tabla hash de pares clave-valor, siendo la "clave" del tipo
sysname
(es decirNVARCHAR(128)
) y el "valor"SQL_VARIANT
. Sentido:CONTEXT_INFO()
(para más detalles, consulte mi publicación: ¿Por qué no CONTEXT_INFO () devuelve el valor exacto establecido por SET CONTEXT_INFO? )CONTEXT_INFO
)Para más detalles, consulte las siguientes páginas de documentación:
fuente
@@SPID
. Esta es la memoria PER-Session / Connection. Una sesión no puede sobrescribir la información de contexto de otra sesión. Y cuando la sesión cierra la sesión, el valor desaparece. No existe un "elemento previamente establecido".No puede hacerlo de esa manera, a menos que esté buscando registrar el ID de usuario del servidor SQL en lugar de una aplicación de nivel uno.
Puede hacer una eliminación suave al tener una columna llamada DeletedBy y configurarla según sea necesario, luego su activador de actualización puede hacer la eliminación real (o archivar el registro, generalmente evito las eliminaciones duras cuando sea posible y legal), así como actualizar su seguimiento de auditoría . Para forzar que se realicen eliminaciones de esa manera, defina un
on delete
activador que genere un error. Si no desea agregar una columna a su tabla física, puede definir una vista que agregue la columna y definirinstead of
desencadenantes para manejar la actualización de la tabla base, pero eso puede ser excesivo.fuente
SPARSE
columna de SQL Server ?Sí, aparentemente hay dos formas ;-). Si hay alguna reserva sobre el uso
CONTEXT_INFO
como he sugerido en mi otra respuesta aquí , solo pensé en otra forma que tenga una separación funcional más limpia de otros códigos / procesos: use una tabla temporal local.El nombre de la tabla temporal debe incluir el nombre de la tabla que se está eliminando, ya que ayudará a mantenerlo separado de cualquier otro código que pueda ejecutarse en la misma sesión. Algo en la línea de:
#<TableName>DeleteAudit
Una de las ventajas de una tabla temporal local
CONTEXT_INFO
es que si alguien en otro proceso, que de alguna manera es llamado desde este proceso "Eliminar" en particular, simplemente utiliza incorrectamente el mismo nombre de tabla temporal, el subproceso a) creará un nuevo tabla temporal del nombre solicitado que estará separada de esta tabla temporal inicial (aunque tenga el mismo nombre), y b) cualquier instrucción DML contra la nueva tabla temporal local en el subproceso no afectará ningún dato en el tabla temporal local creada aquí en el proceso padre, por lo tanto, no se sobrescriben los datos. Por supuesto, si una serie de cuestiones de subproceso una sentencia DML en contra de este nombre de tabla temporal sin emitir una primera Crear una tabla de ese mismo nombre, entonces esas instrucciones DML se afectará a los datos de este cuadro. PERO, en este punto nos estamos poniendo realmenteborde-casey aquí, incluso más que con la probabilidad de usos superpuestos deCONTEXT_INFO
(sí, sé que ha sucedido, por eso digo "borde-caso" en lugar de "nunca sucederá").La aplicación debe llamar a un procedimiento almacenado "Eliminar" que pasa el nombre de usuario (o lo que sea) que está eliminando el registro. Supongo que este ya es el modelo que se está utilizando, ya que parece que ya está rastreando las operaciones de Insertar y Actualizar.
El procedimiento almacenado "Eliminar" hace:
El disparador de auditoría hace:
He probado este código en un disparador y funciona como se esperaba.
fuente