sql-server - example - trigger sql server update
Tabla de historial de SQL Server: ¿se completa a través de SP o Trigger? (11)
Como todos dijeron, Disparadores. Son más fáciles de probar y más resistentes para los usuarios avanzados con acceso inesperado directamente a las tablas que realizan consultas al azar.
En cuanto a más rápido? Determinar qué es rápido dentro de una base de datos es un problema difícil con un gran número de variables. A falta de "probarlo en ambos sentidos y comparar", no obtendrá una respuesta útil sobre qué método es más rápido. Las variables incluyen el tamaño de las tablas involucradas, el patrón normal de actualizaciones, la velocidad de los discos en el servidor, la cantidad de memoria, la cantidad de memoria dedicada al almacenamiento en caché, etc. Esta lista es interminable y cada variable afecta si los disparadores son más rápidos que el SQL personalizado dentro del SP.
Bueno. Rápido. Barato. Elige dos. Los desencadenantes son buenos en términos de integridad y probablemente sean económicos en términos de mantenimiento. Podría decirse que también son rápidos porque una vez que trabajan, terminaste con ellos. Los SP son un problema de mantenimiento y llevar cosas al mantenimiento puede ser rápido, pero nunca es bueno o barato.
Buena suerte.
En mi servidor SQL Server para mi aplicación, quiero crear tablas de historial para varias de mis tablas de claves, que rastrearán un historial de cambios en las filas.
Toda mi aplicación utiliza procedimientos almacenados, no hay SQL incorporado. La única conexión a la base de datos para modificar estas tablas será a través de la aplicación y la interfaz SP. Tradicionalmente, las tiendas con las que he trabajado han realizado esta tarea utilizando desencadenantes.
Si puedo elegir entre procedimientos almacenados y desencadenantes, ¿cuál es mejor? ¿Cual es mas rápido?
Depende de la naturaleza de la aplicación y la estructura de la tabla, el número de índices, el tamaño de los datos, etc., claves externas, etc. Si se trata de tablas relativamente simples (sin o con pocos índices como índices en columnas datetime / integer) con datos limitados set (<1 Million rows), probablemente estarás bien para usar disparadores.
Tenga en cuenta que los desencadenantes pueden ser la fuente de problemas de bloqueo. Supongo que si utiliza las tablas de historial como un tipo de pista de auditoría, las indexará para futuras referencias. Si el activador actualiza la tabla de historial que tarda en insertarse / actualizarse / eliminarse debido a los índices, la llamada al procedimiento se bloqueará hasta que finalice el desencadenador. Además, si hay restricciones de clave externa que se actualizarán en el desencadenador, esto también podría obstaculizar el rendimiento.
En este caso, todo depende de los índices de la tabla. Usamos Sql Server 2000 para una aplicación 24/7 que procesa más de 100K transacciones financieras por día. La tabla más grande / principal tiene más de 100 millones de filas y 15 índices (las eliminaciones masivas no son razonablemente posibles si se desea el tiempo de actividad). Aunque todo el SQL se realiza en Procedimientos almacenados, no utilizamos disparadores o claves foráneas debido al golpe de rendimiento.
Disparadores. Aquí está mi enfoque:
- Cree una tabla de auditoría para cada tabla crítica que requiera una prueba de auditoría
- La tabla de auditoría incluirá todas las columnas de la tabla fuente + información de registros de auditoría de auditoría, como quién, cuándo y la acción
- Dispara solo para ACTUALIZAR y ELIMINAR, la operación INSERTAR tendrá el registro prístino en la tabla fuente misma
- Antes de actualizar o eliminar, copie el registro original + información de auditoría a la tabla de auditoría
- (Opcionalmente, solo para UPDATE :) Para saber qué columna se actualizó, use UPDATE (ColumnName) o COLUMNS_UPDATED () función SQL integrada para determinar las columnas afectadas
La auditoría de esta manera mantiene el estado actual en la tabla fuente y todo el historial en la tabla de auditoría y se identifica fácilmente por las columnas clave.
Disparadores. En este momento, es posible que pueda decir que la única forma en que se actualizan los datos es a través de sus SP, pero las cosas pueden cambiar o es posible que deba hacer una inserción / actualización masiva que usar los SP será demasiado engorrosa. Ir con disparadores.
Disparadores.
Escribimos una GUI (internamente llamada Red Matrix Reloaded ) para permitir la fácil creación / administración de desencadenantes de registro de auditoría.
Aquí hay un poco de DDL de las cosas usadas:
La tabla AuditLog
CREATE TABLE [AuditLog] (
[AuditLogID] [int] IDENTITY (1, 1) NOT NULL ,
[ChangeDate] [datetime] NOT NULL CONSTRAINT [DF_AuditLog_ChangeDate] DEFAULT (getdate()),
[RowGUID] [uniqueidentifier] NOT NULL ,
[ChangeType] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[TableName] [varchar] (128) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[FieldName] [varchar] (128) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[OldValue] [varchar] (8000) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[NewValue] [varchar] (8000) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[Username] [varchar] (128) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[Hostname] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[AppName] [varchar] (128) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[UserGUID] [uniqueidentifier] NULL ,
[TagGUID] [uniqueidentifier] NULL ,
[Tag] [varchar] (8000) COLLATE SQL_Latin1_General_CP1_CI_AS NULL
)
Disparador para registrar inserciones
CREATE TRIGGER LogInsert_Nodes ON dbo.Nodes
FOR INSERT
AS
/* Load the saved context info UserGUID */
DECLARE @SavedUserGUID uniqueidentifier
SELECT @SavedUserGUID = CAST(context_info as uniqueidentifier)
FROM master.dbo.sysprocesses
WHERE spid = @@SPID
DECLARE @NullGUID uniqueidentifier
SELECT @NullGUID = ''{00000000-0000-0000-0000-000000000000}''
IF @SavedUserGUID = @NullGUID
BEGIN
SET @SavedUserGUID = NULL
END
/*We dont'' log individual field changes Old/New because the row is new.
So we only have one record - INSERTED*/
INSERT INTO AuditLog(
ChangeDate, RowGUID, ChangeType,
Username, HostName, AppName,
UserGUID,
TableName, FieldName,
TagGUID, Tag,
OldValue, NewValue)
SELECT
getdate(), --ChangeDate
i.NodeGUID, --RowGUID
''INSERTED'', --ChangeType
USER_NAME(), HOST_NAME(), APP_NAME(),
@SavedUserGUID, --UserGUID
''Nodes'', --TableName
'''', --FieldName
i.ParentNodeGUID, --TagGUID
i.Caption, --Tag
null, --OldValue
null --NewValue
FROM Inserted i
Desencadenar para registrar actualizaciones
CREATE TRIGGER LogUpdate_Nodes ON dbo.Nodes
FOR UPDATE AS
/* Load the saved context info UserGUID */
DECLARE @SavedUserGUID uniqueidentifier
SELECT @SavedUserGUID = CAST(context_info as uniqueidentifier)
FROM master.dbo.sysprocesses
WHERE spid = @@SPID
DECLARE @NullGUID uniqueidentifier
SELECT @NullGUID = ''{00000000-0000-0000-0000-000000000000}''
IF @SavedUserGUID = @NullGUID
BEGIN
SET @SavedUserGUID = NULL
END
/* ParentNodeGUID uniqueidentifier */
IF UPDATE (ParentNodeGUID)
BEGIN
INSERT INTO AuditLog(
ChangeDate, RowGUID, ChangeType,
Username, HostName, AppName,
UserGUID,
TableName, FieldName,
TagGUID, Tag,
OldValue, NewValue)
SELECT
getdate(), --ChangeDate
i.NodeGUID, --RowGUID
''UPDATED'', --ChangeType
USER_NAME(), HOST_NAME(), APP_NAME(),
@SavedUserGUID, --UserGUID
''Nodes'', --TableName
''ParentNodeGUID'', --FieldName
i.ParentNodeGUID, --TagGUID
i.Caption, --Tag
d.ParentNodeGUID, --OldValue
i.ParentNodeGUID --NewValue
FROM Inserted i
INNER JOIN Deleted d
ON i.NodeGUID = d.NodeGUID
WHERE (d.ParentNodeGUID IS NULL AND i.ParentNodeGUID IS NOT NULL)
OR (d.ParentNodeGUID IS NOT NULL AND i.ParentNodeGUID IS NULL)
OR (d.ParentNodeGUID <> i.ParentNodeGUID)
END
/* Caption varchar(255) */
IF UPDATE (Caption)
BEGIN
INSERT INTO AuditLog(
ChangeDate, RowGUID, ChangeType,
Username, HostName, AppName,
UserGUID,
TableName, FieldName,
TagGUID, Tag,
OldValue, NewValue)
SELECT
getdate(), --ChangeDate
i.NodeGUID, --RowGUID
''UPDATED'', --ChangeType
USER_NAME(), HOST_NAME(), APP_NAME(),
@SavedUserGUID, --UserGUID
''Nodes'', --TableName
''Caption'', --FieldName
i.ParentNodeGUID, --TagGUID
i.Caption, --Tag
d.Caption, --OldValue
i.Caption --NewValue
FROM Inserted i
INNER JOIN Deleted d
ON i.NodeGUID = d.NodeGUID
WHERE (d.Caption IS NULL AND i.Caption IS NOT NULL)
OR (d.Caption IS NOT NULL AND i.Caption IS NULL)
OR (d.Caption <> i.Caption)
END
...
/* ImageGUID uniqueidentifier */
IF UPDATE (ImageGUID)
BEGIN
INSERT INTO AuditLog(
ChangeDate, RowGUID, ChangeType,
Username, HostName, AppName,
UserGUID,
TableName, FieldName,
TagGUID, Tag,
OldValue, NewValue)
SELECT
getdate(), --ChangeDate
i.NodeGUID, --RowGUID
''UPDATED'', --ChangeType
USER_NAME(), HOST_NAME(), APP_NAME(),
@SavedUserGUID, --UserGUID
''Nodes'', --TableName
''ImageGUID'', --FieldName
i.ParentNodeGUID, --TagGUID
i.Caption, --Tag
(SELECT Caption FROM Nodes WHERE NodeGUID = d.ImageGUID), --OldValue
(SELECT Caption FROM Nodes WHERE NodeGUID = i.ImageGUID) --New Value
FROM Inserted i
INNER JOIN Deleted d
ON i.NodeGUID = d.NodeGUID
WHERE (d.ImageGUID IS NULL AND i.ImageGUID IS NOT NULL)
OR (d.ImageGUID IS NOT NULL AND i.ImageGUID IS NULL)
OR (d.ImageGUID <> i.ImageGUID)
END
Disparador para iniciar sesión Eliminar
CREATE TRIGGER LogDelete_Nodes ON dbo.Nodes
FOR DELETE
AS
/* Load the saved context info UserGUID */
DECLARE @SavedUserGUID uniqueidentifier
SELECT @SavedUserGUID = CAST(context_info as uniqueidentifier)
FROM master.dbo.sysprocesses
WHERE spid = @@SPID
DECLARE @NullGUID uniqueidentifier
SELECT @NullGUID = ''{00000000-0000-0000-0000-000000000000}''
IF @SavedUserGUID = @NullGUID
BEGIN
SET @SavedUserGUID = NULL
END
/*We dont'' log individual field changes Old/New because the row is new.
So we only have one record - DELETED*/
INSERT INTO AuditLog(
ChangeDate, RowGUID, ChangeType,
Username, HostName, AppName,
UserGUID,
TableName, FieldName,
TagGUID, Tag,
OldValue,NewValue)
SELECT
getdate(), --ChangeDate
d.NodeGUID, --RowGUID
''DELETED'', --ChangeType
USER_NAME(), HOST_NAME(), APP_NAME(),
@SavedUserGUID, --UserGUID
''Nodes'', --TableName
'''', --FieldName
d.ParentNodeGUID, --TagGUID
d.Caption, --Tag
null, --OldValue
null --NewValue
FROM Deleted d
Y para saber qué usuario en el software hizo la actualización, cada conexión "se registra en SQL Server" llamando a un procedimiento almacenado:
CREATE PROCEDURE dbo.SaveContextUserGUID @UserGUID uniqueidentifier AS
/* Saves the given UserGUID as the session''s "Context Information" */
IF @UserGUID IS NULL
BEGIN
PRINT ''Emptying CONTEXT_INFO because of null @UserGUID''
DECLARE @BinVar varbinary(128)
SET @BinVar = CAST( REPLICATE( 0x00, 128 ) AS varbinary(128) )
SET CONTEXT_INFO @BinVar
RETURN 0
END
DECLARE @UserGUIDBinary binary(16) --a guid is 16 bytes
SELECT @UserGUIDBinary = CAST(@UserGUID as binary(16))
SET CONTEXT_INFO @UserGUIDBinary
/* To load the guid back
DECLARE @SavedUserGUID uniqueidentifier
SELECT @SavedUserGUID = CAST(context_info as uniqueidentifier)
FROM master.dbo.sysprocesses
WHERE spid = @@SPID
select @SavedUserGUID AS UserGUID
*/
Notas
- El formato de código elimina la mayoría de las líneas en blanco, por lo que el formateo es una mierda
- Usamos una tabla de usuarios, no seguridad integrada
- Este código se proporciona como una conveniencia, no se permite ninguna crítica de nuestra selección de diseño. Los puristas pueden insistir en que todo el código de registro debe hacerse en la capa de negocios; pueden venir aquí y escribir / mantenerlo para nosotros.
- blobs no se pueden registrar usando desencadenadores en SQL Server (no hay una versión "anterior" de un blob; solo hay lo que es). Text y nText son blobs, lo que hace que las notas no se puedan abrir o que sean varchar (2000).
- la columna Etiqueta se usa como texto arbitrario para identificar la fila (por ejemplo, si se eliminó un cliente, la etiqueta mostrará "General Motors North America" en la tabla de registro de auditoría).
- TagGUID se usa para apuntar al "padre" de la fila. Por ejemplo, el registro de InvoiceLineItems apunta al Facturador . De esta forma, cualquier persona que busque entradas de registro de auditoría relacionadas con una factura específica encontrará las "líneas de pedido" eliminadas por TagGUID de la línea de pedido en la pista de auditoría.
a veces los valores "OldValue" y "NewValue" se escriben como una sub selección, para obtener una cadena significativa. es decir"
OldValue: {233d-ad34234 ..} NewValue: {883-sdf34 ...}
es menos útil en el seguimiento de auditoría que:
OldValue: Daimler Chrysler
NewValue: Cerberus Capital Management
Nota final : no dude en no hacer lo que hacemos. Esto es genial para nosotros, pero todos los demás son libres de no usarlo.
El enfoque recomendado depende de sus requisitos. Si la tabla de historial está allí para el seguimiento de auditoría , debe capturar cada operación. Si la tabla de historial es solo por motivos de rendimiento , entonces un trabajo de transferencia de datos del Agente SQL programado debería ser suficiente.
Para capturar cada operación, use DESPUÉS DE ACTIVAR o Cambiar captura de datos.
Después de los desencadenadores le proporcionan dos tablas temporales para operar dentro del desencadenador:
- INSERTADO después de INSERTAR o ACTUALIZAR
- ELIMINADO después de ELIMINAR
Puede realizar inserciones en la tabla de historial desde estas tablas temporales y su tabla de historial siempre estará actualizada. Es posible que desee agregar numeración de versión, marcas de tiempo o ambas en la tabla de historial para separar los cambios en una sola fila de origen.
Change Data Capture (CDC) está diseñado para crear una tabla delta que puede usar como fuente para cargar datos en un almacén de datos (o una tabla de historial). A diferencia de los desencadenadores, CDC es asincrónico y puede utilizar cualquier método y programación para poblar su destino (sprocs, SSIS).
Puede acceder tanto a los datos originales como a los cambios con CDC. Change Tracking (CT) solo detecta filas cambiadas. Es posible construir un seguimiento de auditoría completo con CDC pero no con CT. CDC y CT solo están disponibles en MSSQL 2008 Enterprise y Developer Editions.
Prefiero usar desencadenantes para tablas de auditoría porque los desencadenadores pueden capturar todas las actualizaciones, inserciones y eliminaciones, no solo las actualizaciones, inserciones y eliminaciones invocadas a través de ciertos procedimientos almacenados:
CREATE TRIGGER [dbo].[tr_Employee_rev]
ON [dbo].[Employee]
AFTER UPDATE, INSERT, DELETE
AS
BEGIN
IF EXISTS(SELECT * FROM INSERTED) AND EXISTS (SELECT * FROM DELETED)
BEGIN
INSERT INTO [EmployeeRev](EmployeeID,Firstname,Initial,Surname,Birthdate,operation, updated, updatedby) SELECT inserted.ID, inserted.Firstname,inserted.Initial,inserted.Surname,inserted.Birthdate,''u'', GetDate(), SYSTEM_USER FROM INSERTED
END
IF EXISTS (SELECT * FROM INSERTED) AND NOT EXISTS(SELECT * FROM DELETED)
BEGIN
INSERT INTO [EmployeeRev](EmployeeID,Firstname,Initial,Surname,Birthdate,operation, updated, updatedby) SELECT inserted.ID, inserted.Firstname,inserted.Initial,inserted.Surname,inserted.Birthdate,''i'', GetDate(), SYSTEM_USER FROM INSERTED
END
IF EXISTS(SELECT * FROM DELETED) AND NOT EXISTS(SELECT * FROM INSERTED)
BEGIN
INSERT INTO [EmployeeRev](EmployeeID,Firstname,Initial,Surname,Birthdate,operation, updated, updatedby) SELECT deleted.ID, deleted.Firstname,deleted.Initial,deleted.Surname,deleted.Birthdate,''d'', GetDate(), SYSTEM_USER FROM DELETED
END
END
Yo uso SQLServer para generar el SQL para las tablas de revisión en lugar de codificarlo a mano. Este código está disponible en https://github.com/newdigate/sqlserver-revision-tables
Tenemos una herramienta de terceros ApexSQL Audit que utilizamos para generar activadores.
Así es cómo se ven los disparadores en el fondo y cómo se almacenan los datos. Con suerte, los muchachos encontrarán esto lo suficientemente útil como para realizar un proceso de ingeniería inversa. Es un poco diferente de lo que Ian Boyd mostró en sus ejemplos porque permite que cada columna sea auditada por separado.
Tabla 1: contiene los detalles de la transacción (quién, cuándo, aplicación, nombre de host, etc.)
CREATE TABLE [dbo].[AUDIT_LOG_TRANSACTIONS](
[AUDIT_LOG_TRANSACTION_ID] [int] IDENTITY(1,1) NOT NULL,
[DATABASE] [nvarchar](128) NOT NULL,
[TABLE_NAME] [nvarchar](261) NOT NULL,
[TABLE_SCHEMA] [nvarchar](261) NOT NULL,
[AUDIT_ACTION_ID] [tinyint] NOT NULL,
[HOST_NAME] [varchar](128) NOT NULL,
[APP_NAME] [varchar](128) NOT NULL,
[MODIFIED_BY] [varchar](128) NOT NULL,
[MODIFIED_DATE] [datetime] NOT NULL,
[AFFECTED_ROWS] [int] NOT NULL,
[SYSOBJ_ID] AS (object_id([TABLE_NAME])),
PRIMARY KEY CLUSTERED
(
[AUDIT_LOG_TRANSACTION_ID] ASC
)
)
Tabla 2 - contiene valores anteriores / posteriores.
CREATE TABLE [dbo].[AUDIT_LOG_DATA](
[AUDIT_LOG_DATA_ID] [int] IDENTITY(1,1) NOT NULL,
[AUDIT_LOG_TRANSACTION_ID] [int] NOT NULL,
[PRIMARY_KEY_DATA] [nvarchar](1500) NOT NULL,
[COL_NAME] [nvarchar](128) NOT NULL,
[OLD_VALUE_LONG] [ntext] NULL,
[NEW_VALUE_LONG] [ntext] NULL,
[NEW_VALUE_BLOB] [image] NULL,
[NEW_VALUE] AS (isnull(CONVERT([varchar](8000), [NEW_VALUE_LONG],0),CONVERT([varchar](8000),CONVERT([varbinary](8000),substring([NEW_VALUE_BLOB],(1),(8000)),0),0))),
[OLD_VALUE] AS (CONVERT([varchar](8000),[OLD_VALUE_LONG],0)),
[PRIMARY_KEY] AS ([PRIMARY_KEY_DATA]),
[DATA_TYPE] [char](1) NOT NULL,
[KEY1] [nvarchar](500) NULL,
[KEY2] [nvarchar](500) NULL,
[KEY3] [nvarchar](500) NULL,
[KEY4] [nvarchar](500) NULL,
PRIMARY KEY CLUSTERED
(
[AUDIT_LOG_DATA_ID] ASC
)
)
Insertar disparador
No estoy mostrando activadores para la actualización porque son bastante largos y tienen la misma lógica que este.
CREATE TRIGGER [dbo].[tr_i_AUDIT_Audited_Table]
ON [dbo].[Audited_Table]
FOR INSERT
NOT FOR REPLICATION
As
BEGIN
DECLARE
@IDENTITY_SAVE varchar(50),
@AUDIT_LOG_TRANSACTION_ID Int,
@PRIM_KEY nvarchar(4000),
@ROWS_COUNT int
SET NOCOUNT ON
Select @ROWS_COUNT=count(*) from inserted
Set @IDENTITY_SAVE = CAST(IsNull(@@IDENTITY,1) AS varchar(50))
INSERT
INTO dbo.AUDIT_LOG_TRANSACTIONS
(
TABLE_NAME,
TABLE_SCHEMA,
AUDIT_ACTION_ID,
HOST_NAME,
APP_NAME,
MODIFIED_BY,
MODIFIED_DATE,
AFFECTED_ROWS,
[DATABASE]
)
values(
''Audited_Table'',
''dbo'',
2, -- ACTION ID For INSERT
CASE
WHEN LEN(HOST_NAME()) < 1 THEN '' ''
ELSE HOST_NAME()
END,
CASE
WHEN LEN(APP_NAME()) < 1 THEN '' ''
ELSE APP_NAME()
END,
SUSER_SNAME(),
GETDATE(),
@ROWS_COUNT,
''Database_Name''
)
Set @AUDIT_LOG_TRANSACTION_ID = SCOPE_IDENTITY()
--This INSERT INTO code is repeated for each columns that is audited.
--Below are examples for only two columns
INSERT INTO dbo.AUDIT_LOG_DATA
(
AUDIT_LOG_TRANSACTION_ID,
PRIMARY_KEY_DATA,
COL_NAME,
NEW_VALUE_LONG,
DATA_TYPE
, KEY1
)
SELECT
@AUDIT_LOG_TRANSACTION_ID,
convert(nvarchar(1500), IsNull(''[PK_Column]=''+CONVERT(nvarchar(4000), NEW.[PK_Column], 0), ''[PK_Column] Is Null'')),
''Column1'',
CONVERT(nvarchar(4000), NEW.[Column1], 0),
''A''
, CONVERT(nvarchar(500), CONVERT(nvarchar(4000), NEW.[PK_Column], 0))
FROM inserted NEW
WHERE NEW.[Column1] Is Not Null
--value is inserted for each column that is selected for auditin
INSERT INTO dbo.AUDIT_LOG_DATA
(
AUDIT_LOG_TRANSACTION_ID,
PRIMARY_KEY_DATA,
COL_NAME,
NEW_VALUE_LONG,
DATA_TYPE
, KEY1
)
SELECT
@AUDIT_LOG_TRANSACTION_ID,
convert(nvarchar(1500), IsNull(''[PK_Column]=''+CONVERT(nvarchar(4000), NEW.[PK_Column], 0), ''[PK_Column] Is Null'')),
''Column2'',
CONVERT(nvarchar(4000), NEW.[Column2], 0),
''A''
, CONVERT(nvarchar(500), CONVERT(nvarchar(4000), NEW.[PK_Column], 0))
FROM inserted NEW
WHERE NEW.[Column2] Is Not Null
End
Descargo de responsabilidad: no estoy afiliado a Apex de ninguna manera, pero sí uso sus herramientas en mi trabajo actual.
Un tema que debe tener mucho cuidado es identificar los casos de uso previstos para esta tabla, y asegúrese de que esté construido correctamente para ese fin.
Específicamente, si se trata de un seguimiento de auditoría operacional para las partes interesadas, eso es bastante diferente de las instantáneas de antes y después de los cambios de registro en las tablas. (De hecho, me resulta difícil imaginar un buen uso para cambios de registro, aparte de la depuración).
Una pista de auditoría normalmente requiere, como mínimo, una identificación de usuario, una marca de tiempo y un código de operación, y probablemente algunos detalles sobre la operación. Ejemplo: cambie la cantidad pedida en un artículo de línea en una orden de compra.
Y para este tipo de pista de auditoría , no desea utilizar activadores. Mientras más alto en la capa BR inserte la generación de estos eventos, mejor.
OTOH, para los cambios de nivel de registro, los desencadenantes son la combinación adecuada. Pero a menudo también es más fácil obtener esto de tus archivos de diario dbms.
Use disparadores para esto. Esto significa que cualquier cambio, independientemente de la fuente, se reflejará en la tabla de historial. Es bueno para la seguridad, resistente a los modos de falla, como las personas que olvidan agregar código para actualizar la tabla de historial, etc.
No es probable que haya ninguna diferencia de velocidad particular en ninguno de los dos para este tipo de operación ya que el tiempo de ejecución estará dominado por la E / S.
en SQL Server 2008 una nueva característica llamada CDC (Change Data Capture) CDC en MSDN puede ayudar. CDC es la capacidad de registrar cambios en los datos de la tabla en otra tabla sin escribir disparadores o algún otro mecanismo. Cambiar la captura de datos registra los cambios como insertar, actualizar y eliminar en una tabla en el servidor SQL, haciendo que los detalles de los cambios estén disponibles en formato.