Está en la página 1de 6

El siguiente trigger permite auditar los cambios (Inserciones, Actualizaciones y eliminaciones) a una

tabla. Es un trigger genérico. Es decir, el mismo trigger funciona para cualquier tabla de la base de datos.
Hay que tener en cuenta solo tres cosas:
1. Se debe crear una tabla de log para almacenar la información de auditoría.
2. En el script hay que cambiar manualmente el nombre de la tabla sobre la cual se esta creando el
trigger.
3. Solo funciona con tablas que tienen llave primaria.
Este trigger fue diseñado por Nigel Rivett y funciona con las versiones 2005 en adelante. He hecho
cambios mínimos de forma y he traducido e incluido comentarios para hacer mas legible y entendible el
script.
Lo primero que hay que hacer es crear la tabla de auditoría, sobre la cual el trigger escribirá el detalle de
las transacciones realizadas:
CREATETABLEdbo.logTransacciones(
TipoTrnchar(1),
Tablavarchar(128),
PKvarchar(1000),
Campovarchar(128),
ValorOriginalvarchar(1000),
ValorNuevovarchar(1000),
FechaTrndatetime,
Usuariovarchar(128))
GO
.
En esta tabla se almacena el tipo de transacción(I-Insert, U-Update, D-Delete), el nombre de la tabla, la
llave del registro, el o los campos que han sufrido algún cambio, el valor original y el valor nuevo para
las actualizaciones y por ultimo la fecha en la que se hizo el cambio y el usuario que lo ejecutó. Cualquier
cambio realizado en cualquier tabla de la base de datos que tenga el trigger, será almacenado aquí
Vamos ahora a crear la tabla Cliente, que nos servirá para probar el trigger. Sobre esta tabla ejecutaremos
el script para crearle un trigger y haremos los cambios para ver como se van grabando los datos en la
tabla de auditoría:
CREATETABLEdbo.Cliente(
IdClienteintIDENTITY(1,1)notnull,
Nombresvarchar(100),
Apellidosvarchar(100),
TipoDocumentochar(3),
NumeroDocumentovarchar(15))
GO

ALTERTABLEdbo.ClienteADDCONSTRAINTPK_ClientePRIMARYKEY (IdCliente)
GO
.
A continuación el script del trigger genérico. Tener mucha precaución de actualizar manualmente el
nombre de la tabla al momento de almacenar el valor de la variable @TableName:
NOTA: para bajar el script del trigger hacer clic aquí
/*
————————————————————————-
PROPOSITO | Capturar los cambios realizados en la tabla.
————————————————————————-
NOTAS | -Solo crearlo en tablas donde realmente se necesita auditar
| -La tabla que se desea auditar debe tener llave primaria
| -Previamente se requiere crear la siguiente tabla:
| CREATE TABLE dbo.logTransacciones (
| TipoTrn char(1), Tabla varchar(128),
| PK varchar(1000), Campo varchar(128),
| ValorOriginal varchar(1000), ValorNuevo varchar(1000),
| FechaTrn datetime, Usuario varchar(128))
| -Cambiar valor de @TableName para coincidir con tabla que se
| desea auditar
————————————————————————-
PARAMETROS DE ENTRADA| NA
————————————————————————-
PARAMETROS DE SALIDA | NA
————————————————————————-
CREADO POR | Nigel Rivett
FECHA CREACION | ND
————————————————————————-
HISTORIAL DE CAMBIOS | FECHA RESPONSABLE MOTIVO
| ———- —————– ———————
| 15/02/2010 Alberto De Rossi -Cambio de querys a
| estandar ANSI.
| -Traducción de
| comentarios al
| castelllano
| -Arreglos de forma
————————————————————————-
*/
ALTERTRIGGERdbo.trIUDClienteONClienteFORINSERT,UPDATE,DELETE
AS

DECLARE@bitint,
@fieldint,
@maxfieldint,
@charint,
@fieldnamevarchar(128),
@TableNamevarchar(128),
@PKColsvarchar(1000),
@sqlvarchar(2000),
@UpdateDatevarchar(21),
@UserNamevarchar(128),
@Typechar(1),
@PKSELECTvarchar(1000)

SELECT@TableName=‘Cliente’–<– cambiar el nombre de la tabla

— Fecha y Usuario
SELECT@UserName=system_user,
@UpdateDate=convert(varchar(8),getdate(), 112)+
‘ ‘+
convert(varchar(12),getdate(), 114)

SETNoCountON

–Identificar que evento se está ejecutando (Insert, Update o Delete)


–en base a cursores especiales (inserted y deleted)
ifexists(SELECT*FROMinserted)
ifexists(SELECT*FROMdeleted)–Si es un update
SELECT@Type=‘U’
else –Si es un insert
SELECT@Type=‘I’
else –si es un delete
SELECT@Type=‘D’

— Obtenemos la lista de columnas de los cursores


SELECT*into#insfrominserted
SELECT*into#delfromdeleted

— Obtener las columnas de llave primaria


SELECT@PKCols=coalesce(@PKCols+‘ and’,‘ on’)+
‘ i.’+
c.COLUMN_NAME+‘ = d.’+
c.COLUMN_NAME
FROMINFORMATION_SCHEMA.TABLE_CONSTRAINTSpk
JOININFORMATION_SCHEMA.KEY_COLUMN_USAGEc
ONc.TABLE_NAME=pk.TABLE_NAME
ANDc.CONSTRAINT_NAME=pk.CONSTRAINT_NAME
WHEREpk.TABLE_NAME=@TableNameAND
pk.CONSTRAINT_TYPE=‘PRIMARY KEY’

— Obtener la llave primaria y columnas para la inserción en la tabla de auditoria


SELECT
@PKSELECT=coalesce(@PKSelect+‘+’,”)+
”'<‘+
COLUMN_NAME+
‘=”+convert(varchar(100),coalesce(i.’+
COLUMN_NAME+‘,d.’+
COLUMN_NAME+‘))+”>”’
FROMINFORMATION_SCHEMA.TABLE_CONSTRAINTSpk
JOININFORMATION_SCHEMA.KEY_COLUMN_USAGEc
ONc.TABLE_NAME=pk.TABLE_NAME
ANDc.CONSTRAINT_NAME=pk.CONSTRAINT_NAME
WHEREpk.TABLE_NAME=@TableName
ANDCONSTRAINT_TYPE=‘PRIMARY KEY’

if@PKColsisnull–<– Este trigger solo funciona si la tabla tiene llave primaria


BEGIN
RAISERROR(‘no PK on table %s’, 16,–1,@TableName)
RETURN
END

–Loop para armar el query de inserción en la tabla de log.


–Un registro por cada campo afectado.
SELECT
@field= 0,
@maxfield=max(ORDINAL_POSITION)
FROMINFORMATION_SCHEMA.COLUMNS
WHERETABLE_NAME=@TableName

while@field<@maxfield
BEGIN
SELECT@field=min(ORDINAL_POSITION)
FROMINFORMATION_SCHEMA.COLUMNS
WHERETABLE_NAME=@TableNameandORDINAL_POSITION>@field
SELECT@bit=(@field– 1 )% 8 + 1
SELECT@bit=power(2,@bit– 1)
SELECT@char=((@field– 1)/ 8)+ 1
ifsubstring(COLUMNS_UPDATED(),@char, 1)&@bit> 0
or@Typein(‘I’,‘D’)
BEGIN
SELECT@fieldname=COLUMN_NAME
FROMINFORMATION_SCHEMA.COLUMNS
WHERETABLE_NAME=@TableNameandORDINAL_POSITION=@field
SELECT@sql=‘insert LogTransacciones (TipoTrn, Tabla, PK, Campo, ValorOriginal, ValorNuevo,
FechaTrn, Usuario)’
SELECT@sql=@sql+ ‘ SELECT ”’+@Type+””
SELECT@sql=@sql+ ‘,”’+@TableName+””
SELECT@sql=@sql+ ‘,’+@PKSelect
SELECT@sql=@sql+ ‘,”’+@fieldname+””
SELECT@sql=@sql+ ‘,convert(varchar(1000),d.’+@fieldname+‘)’
SELECT@sql=@sql+ ‘,convert(varchar(1000),i.’+@fieldname+‘)’
SELECT@sql=@sql+ ‘,”’+@UpdateDate+””
SELECT@sql=@sql+ ‘,”’+@UserName+””
SELECT@sql=@sql+ ‘ from #ins i full outer join #del d’
SELECT@sql=@sql+ @PKCols
SELECT@sql=@sql+ ‘ where i.’+@fieldname+‘ <> d.’+@fieldname
SELECT@sql=@sql+ ‘ or (i.’+@fieldname+‘ is null and d.’+@fieldname+‘ is not null)’
SELECT@sql=@sql+ ‘ or (i.’+@fieldname+‘ is not null and d.’+@fieldname+‘ is null)’
exec (@sql)
END
END

SETNoCountOFF
GO
.
Ahora vamos a probar el trigger. Vamos a insertar un registro en la tabla Cliente:
–Primera insersión
INSERTINTOCliente(Nombres,Apellidos,TipoDocumento,NumeroDocumento)
VALUES (‘Guillermo’,‘Morales Dueñas’,‘DNI’,‘03247159’)
GO

SELECT*FROMCLiente
GO

SELECT*FROMLogTransacciones
GO

.
Obsérvese que en la tabla de auditoría se han creado un registro por cada campo del registro que se ha
insertado en la tabla Cliente. El tipo de transacción es I de Insert y el campo Valor Original es nulo por
que este es un nuevo registro. Hagamos una segunda prueba.
–Segunda insersión
INSERTINTOCliente(Nombres,Apellidos,TipoDocumento,NumeroDocumento)
VALUES (‘Ana’,‘Sanchez Maldonado’,‘DNI’,‘18342711’)
GO

SELECT*FROMCLiente
GO

SELECT*FROMLogTransacciones
GO

.
Nuevamente en la tabla de auditoría se han creado un registro por cada campo del registro que se ha
insertado en la tabla Cliente. Pero aquí se observa que es dificil identificar cuales de los registros de la
tabla de auditoría pertence a cada transacción de inserción. La unica forma de identificarlo es con el
campo PK, que indica la llave primaria del registro insertado, ya que la llave primaria es única. Quizá
sería util añadir un campo con un número identificador de transacción.
Ahora vamos a probar la tabla con una actualización:
–Actualización de datos
UPDATEClienteSETNombres=‘Ana María’WhereIdCliente= 2
GO

SELECT*FROMCLiente
GO

SELECT*FROMLogTransacciones
GO

.
Obsérvese que solo se ha ingresado un solo registro en la tabla de auditoría, ya que solo se actualizó un
campo de la tabla Cliente. El tipo de transacción es U de Updated y los campos ValorOriginal y
ValorNuevo si tienen datos reflejando el antes y el despues de la actualización. Nuevamente mi
observación de que los datos en esta tabla estan un poco mezclados, cuando hay muchos registros no
debe ser tan facil ubicar cuales perteneces a una transacción en particular.
Por ultimo, probaremos con una eliminación:
–Eliminación de datos
DELETEClienteWhereIdCliente= 1
GO

SELECT*FROMCLiente
GO

SELECT*FROMLogTransacciones
GO

.
Aquí aparece un registro por cada campo del registro eliminado de la tabla Cliente. El tipo de transacción
es D de Deleted y el campo ValorNuevo es nulo por que el registro se ha borrado, solo registra el valor
original
CONCLUSIONES: Esta es una propuesta interesante y practica de Nigel Rivett para auditar una tabla
en particular. Se debe tener en cuenta que no es necesario crear este trigger en todas las tablas de una
base de datos, solo en aquellas a las que se desea auditar. Tener en cuenta tambien que la ejecución del
trigger y el procesamiento de los datos de auditoría añade sobrecarga al procesador y a la memoria, lo
cual puede hacer que el tiempo de respuesta de las transacciones disminuya. Se recomienda monitorear
con Profiler para medir el impacto de implementar el trigger.

También podría gustarte