Estoy buscando una buena forma de registrar los cambios que ocurren en un conjunto particular de tablas en mi base de datos de SQL Server 2005. Creo que la mejor manera de hacerlo es a través de un disparador que se ejecuta en las actualizaciones y eliminaciones. ¿Hay alguna forma de agarrar la declaración real que se está ejecutando? Una vez que tengo la declaración, puedo registrarla fácilmente en otro lugar (otra tabla de DB). Sin embargo, no he encontrado una manera fácil (si es posible) de obtener la declaración SQL que se está ejecutando.Cambios de registro en la tabla de la base de datos con el disparador
Respuesta
Los disparadores son malos, me mantendría alejado de los factores desencadenantes.
Si está intentando solucionar algo, adjunte Sql Profiler a la base de datos con condiciones específicas. Esto registrará cada consulta ejecutada para su inspección.
Otra opción es cambiar al programa que llama para registrar sus consultas. Esta es una práctica muy común.
Desafortunadamente, nuestros DBA no quieren encender Profiler ya que me dijeron que incluso si usa condiciones específicas, todavía tiene que ir a través de cada consulta que llega a la base de datos. Además, no puedo hacerlo desde las aplicaciones en este momento, ya que son heredados y en muchos lugares diferentes. ¿No hay forma de obtener la declaración real desde un disparador? – Jason
¿Dejando de lado su pregunta real, pero trate de mejorar su relación con los DBA? Puede demostrar en un servidor de prueba que Profiler no tiene un gran impacto. Invitar al DBA a una charla con los interesados de las empresas; eso les da una idea de que es serio, y al final del día, la empresa paga el salario del DBA. – Andomar
Finalmente, convenció a los DBA para que activen el Analizador de SQL. Sin embargo, solo se enciende durante una semana. – Jason
Debería poder lograrlo usando el system management views.
Un ejemplo podría ser algo como esto:
SELECT er.session_id,
er.status,
er.command,
DB_NAME(database_id) AS 'DatabaseName',
user_id,
st.text
FROM sys.dm_exec_requests AS er
CROSS APPLY sys.dm_exec_sql_text(er.sql_handle) AS st
WHERE er.session_id = @@SPID;
No estoy seguro de que esto será tan útil para usted como un mecanismo de registro más centrada en los datos podría ser, sin embargo.
+1 Interesante, aunque hacer esto en un gatillo se siente un poco equivocado – Andomar
Esto es genial, no me había dado cuenta de que estos estaban disponibles. Sin embargo, no muestran la declaración que está realizando la actualización, parece que muestran todo el lote. Sigue siendo muy útil y tal vez se ajuste a los requisitos. – Rory
Desafortunadamente, no tenemos un procedimiento almacenado que se esté ejecutando para actualizar las tablas. Este es un sistema antiguo que está teniendo problemas y estamos tratando de diagnosticar quién está ejecutando actualizaciones/eliminaciones y qué declaraciones están ejecutando. – Jason
realmente necesita registrar la declaración que se ejecutó, la mayoría de las personas registran los datos modificados (tablas INSERTED y DELETED dentro del desencadenador).
MSSQL tiene tablas virtuales llamadas 'Insertado' y 'Eliminado', que contienen registros de datos recién insertados y/o recientemente eliminados y/o recientemente actualizados, a los que se puede acceder desde un desencadenador ... Los uso , para saber qué datos han cambiado (eso es en vez de que se les diga qué enunciado cambió los datos).
No olvide que su registro será parte de la transacción, por lo que si hay un error y restituye la transacción, también se eliminará su registro.
Los disparadores son una buena forma de asegurarse de que se registran los cambios, ya que casi siempre se dispararán independientemente de cómo se realicen las actualizaciones, p. conexiones ad-hoc así como conexiones de aplicaciones.
Según lo sugerido por @mwigdahl, las vistas de administración del sistema parecen una buena manera de capturar el lote en ejecución actual. Si eso es particularmente útil para iniciar sesión en el desencadenador es otra cosa.
Una desventaja del uso de desencadenantes es que solo puede identificar el origen de la actualización desde la conexión de la base de datos. Muchas aplicaciones no tienen ninguna información de usuario asociada a la conexión, para facilitar la agrupación de conexiones, por lo que no se sabe qué usuario realiza la acción. es decir, el inicio de sesión utilizado por la conexión es un inicio de sesión genérico de la aplicación en lugar de la persona que usa la aplicación. La forma normal de evitar esto es utilizar procedimientos almacenados como interfaz para toda la interacción con la base de datos, y luego asegurarse de que se pase una UserId con todas las llamadas a procedimientos. A continuación, puede realizar su registro a través del procedimiento almacenado en lugar de un desencadenador. Claramente, esto solo es útil si sabe que las personas no actualizarán las tablas directamente sin utilizar los procedimientos, o no necesitan registrar esa situación.
La capacidad de obtener el lote que se está ejecutando actualmente podría proporcionar un mecanismo aún mejor: si se asegura de que todos sus lotes sql contienen un UserId, puede extraerlo del sql dentro de su desencadenante. Eso le permitiría hacer todo el registro con activadores, lo que significa que captura todo, pero también le permite asociar los cambios con un usuario en particular.
Si vas por el camino de gatillo vale la pena comprobar las situaciones desencadenantes no se disparan (tal vez los datos en bloque cargado? O si las personas tienen permiso para desactivar los factores desencadenantes).
Ten en cuenta también como @idstam señaló que el código de disparo estará dentro de su transacción por lo que normalmente se registran y se deshacen junto con él.
Otra cosa a tener en cuenta cuando se escriben desencadenantes es el comportamiento de @@IDENTITY: si tiene procedimientos que usan @@ IDENTITY, puede cambiar accidentalmente su comportamiento.
Para aquellos que no lo sepan, @@ identity en general no se debe usar en ninguna base de datos con desencadenantes. Si el activador se inserta en una tabla con una identidad, eso es lo que se devuelve, no la identidad de la inserción del registro original. Scope_identity() corrige esto o en las versiones más nuevas de SQl Server puede utilizar la cláusula de salida en su lugar. – HLGEM
tener cuidado aquí, ya que el fuego se dispara al nivel de fila, no el nivel de instrucción SQL. Por lo tanto, si alguien "DELETE FROM BIGTABLE", su disparador se disparará para cada fila en esa tabla (esto específicamente con respecto al hecho de que desea saber la declaración de SQL que realizó la operación, por lo que tendrá que "figurar eso fuera "por cada fila que afecte la afirmación).
¡Esto NO es verdad! Los desencadenantes se disparan una vez por acción si elimina/actualiza/inserta un millón de filas o 1. Esta es la razón por la que todos los desencadenadores deben escribirse para manejar múltiples registros inserta o elimina y no mediante el uso de un cursor (a menos que)! – HLGEM
Si lo que desea es mantener un registro de todas las transacciones (insertar, actualizar y eliminar) en algunas tablas de la base, a continuación, puede ejecutar el siguiente script:
IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= 'Audit')
CREATE TABLE LogTable
(
LogID [int]IDENTITY(1,1) NOT NULL,
Type char(1),
TableName varchar(128),
PrimaryKeyField varchar(1000),
PrimaryKeyValue varchar(1000),
FieldName varchar(128),
OldValue varchar(1000),
NewValue varchar(1000),
UpdateDate datetime DEFAULT (GetDate()),
UserName varchar(128)
)
GO
DECLARE @sql varchar(8000), @TABLE_NAME sysname
SET NOCOUNT ON
SELECT @TABLE_NAME= MIN(TABLE_NAME)
FROM INFORMATION_SCHEMA.Tables
WHERE
--query for table that you want to audit
TABLE_TYPE= 'BASE TABLE'
AND TABLE_NAME!= 'sysdiagrams'
AND TABLE_NAME!= 'LogTable'
AND TABLE_NAME!= 'one table to not record de log';
WHILE @TABLE_NAME IS NOT NULL
BEGIN
SELECT 'PROCESANDO ' + @TABLE_NAME;
EXEC('IF OBJECT_ID (''' + @TABLE_NAME+ '_ChangeTracking'', ''TR'') IS NOT NULL DROP TRIGGER ' + @TABLE_NAME+ '_ChangeTracking')
SELECT @sql = 'create trigger ' + @TABLE_NAME+ '_ChangeTracking on ' + @TABLE_NAME+ ' for insert, update, delete
as
declare
@bit int ,
@field int ,
@maxfield int ,
@char int ,
@fieldname varchar(128) ,
@TableName varchar(128) ,
@PKCols varchar(1000) ,
@sql varchar(2000),
@UpdateDate varchar(21) ,
@UserName varchar(128) ,
@Type char(1) ,
@PKFieldSelect varchar(1000),
@PKValueSelect varchar(1000)
select @TableName = ''' + @TABLE_NAME+ '''
-- date and user
select @UserName = system_user ,
@UpdateDate = convert(varchar(8), getdate(), 112) + '' '' + convert(varchar(12), getdate(), 114)
-- Action
if exists (select * from inserted)
if exists (select * from deleted)
select @Type = ''U''
else
select @Type = ''I''
else
select @Type = ''D''
-- get list of columns
select * into #ins from inserted
select * into #del from deleted
-- Get primary key columns for full outer join
select @PKCols = coalesce(@PKCols + '' and'', '' on'') + '' i.'' + c.COLUMN_NAME + '' = d.'' + c.COLUMN_NAME
from INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk ,
INFORMATION_SCHEMA.KEY_COLUMN_USAGE c
where pk.TABLE_NAME = @TableName
and CONSTRAINT_TYPE = ''PRIMARY KEY''
and c.TABLE_NAME = pk.TABLE_NAME
and c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME
-- Get primary key fields select for insert(comma deparated)
select @PKFieldSelect = coalesce(@PKFieldSelect+''+'','''') + '''''''' + COLUMN_NAME + '',''''''
from INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk ,
INFORMATION_SCHEMA.KEY_COLUMN_USAGE c
where pk.TABLE_NAME = @TableName
and CONSTRAINT_TYPE = ''PRIMARY KEY''
and c.TABLE_NAME = pk.TABLE_NAME
and c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME
-- Get primary key values for insert(comma deparated as varchar)
select @PKValueSelect = coalesce(@PKValueSelect+''+'','''') + ''convert(varchar(100), coalesce(i.'' + COLUMN_NAME + '',d.'' + COLUMN_NAME + ''))'' + ''+'''',''''''
from INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk ,
INFORMATION_SCHEMA.KEY_COLUMN_USAGE c
where pk.TABLE_NAME = @TableName
and CONSTRAINT_TYPE = ''PRIMARY KEY''
and c.TABLE_NAME = pk.TABLE_NAME
and c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME
if @PKCols is null
begin
raiserror(''no PK on table %s'', 16, -1, @TableName)
return
end
select @sql = ''insert LogTable(Type, TableName, PrimaryKeyField, PrimaryKeyValue, UserName)''
select @sql = @sql + '' select '''''' + @Type + ''''''''
select @sql = @sql + '','''''' + @TableName + ''''''''
select @sql = @sql + '','' + @PKFieldSelect
select @sql = @sql + '','' + @PKValueSelect
select @sql = @sql + '','''''' + @UserName + ''''''''
select @sql = @sql + '' from #ins i full outer join #del d''
select @sql = @sql + @PKCols
exec (@sql)
';
SELECT @sql
EXEC(@sql)
SELECT @TABLE_NAME= MIN(TABLE_NAME) FROM INFORMATION_SCHEMA.Tables
WHERE TABLE_NAME> @TABLE_NAME
--query for table that you want to audit
AND TABLE_TYPE= 'BASE TABLE'
AND TABLE_NAME!= 'sysdiagrams'
AND TABLE_NAME!= 'LogTable'
AND TABLE_NAME!= 'one table to not record de log';
END
¿También es bueno para la tabla de sybase? Necesito un fragmento de código para auditar una tabla (estoy usando sybase ASE 15-2 –
Esto no rellena los campos valor antiguo/valor nuevo. Mejor, complete la respuesta aquí http://stackoverflow.com/questions/19737723/log-record- changes-in-sql-server-in-an-audit-table – smirkingman
Hay un modelo para la creación de estos desencadenantes llamados Log Trigger. Esto es independiente del vendedor y muy simple. Se describe en here.
Los cambios se registran en otra tabla de historia. No hay forma de captar la declaración exacta, pero es posible detectar si se trató de una inserción y una actualización o una eliminación porque crea un conjunto de registros "encadenados". Una inserción es un registro sin predecesor, una eliminación es un registro sin sucesor, los registros intermedios son actualizaciones. Se pueden detectar cambios comparando un registro con su predecesor.
Es muy fácil obtener una instantánea de una sola entidad (o toda la tabla) en un punto determinado de tiempo.
Como beneficio adicional, la sintaxis de este patrón para SQL Server pasa a ser la más simple, en comparación con Oracle, DB2 y MySQL.
Es probable que se elimine un enlace sin resumen de contenido, especialmente si el enlace deja de funcionar. –
Utilice un Log Trigger
Hay pocas razones para capturar el SQL real, ya que puede muchas declaraciones diferentes que cambiar los datos de la misma manera.
prueba a instalar alguna herramienta de terceros basada gatillo como ApexSQL Audit y luego ingeniería inversa de cómo lo hacen. Simplemente instálelo en modo de prueba y vea cómo genera activadores para capturar todo tipo de información.
Varias otras cosas a considerar son: la planificación
almacenamiento - si usted tiene una gran cantidad de cambios que significa que tendrá un montón de datos de auditoría. Consideraría almacenar esos datos en bases de datos separadas. Especialmente si planea auditar más de una base de datos.
Gestión de la cantidad de datos - con el tiempo es probable que no estará en la necesidad de mantener unos registros muy antiguos. Planifique la eliminación fácil de datos antiguos
Cambios en el esquema: qué ocurre si se actualiza el esquema. En el peor de los casos, tus disparadores dejarán de funcionar y arrojarán un error si no se crean correctamente.En el mejor de los casos, perderá algunos de los datos. Esto también es algo a considerar.
Teniendo todo esto en cuenta, probablemente sea la solución más efectiva desde el punto de vista del tiempo para crear una solución ya desarrollada.
- 1. Guardar cambios en el campo de la base de datos: ¿prácticas recomendadas? Versiones, Loggable?
- 2. registro aleatorio de la base de datos MySQL con CodeIgniter
- 3. Verificar cambios en la base de datos (control de versión)
- 4. Existe el registro de Android() en la base de datos?
- 5. manteniendo una tabla de registro en la base de datos sqlite?
- 6. ¿Cómo rastrear los cambios en varias columnas en la tabla de la base de datos con fines de auditoría?
- 7. ¿Cómo eliminar el registro de la tabla?
- 8. cambios de registro para cada columna en una tabla
- 9. Liquibase: cómo generar un registro de cambios para la base de datos existente
- 10. Desarrollo ágil y cambios en la base de datos
- 11. versión preliminar de la tabla de la base de datos
- 12. Cómo rastrear cambios de datos en una tabla de base de datos
- 13. Cómo fusionar los cambios en la base de datos Drupal
- 14. problema con la base de datos sqlite, sin tabla:
- 15. truncar toda la tabla en la base de datos mysql
- 16. Duplicar un registro de base de datos con linq
- 17. Recuperar el último registro de la tabla de SQL Server
- 18. ¿Actualizar la tabla de la base de datos de una tabla de base de datos de SQL Server a otra?
- 19. claves externas en la tabla de base de datos diferente
- 20. alterar múltiples bases de datos mysql a la vez (cambios en la base de datos SAAS)
- 21. base de datos de cambios MySQL con valor de variable
- 22. ¿Cómo rastreas los cambios en la base de datos en el control de código fuente?
- 23. ¿Cuál es la mejor manera de mantener el historial de cambios en los campos de la base de datos?
- 24. Creación de desencadenante para la tabla en la base de datos MySQL (error de sintaxis)
- 25. ¿Puedes TOMAR LA TABLA SI EXISTE especificando el nombre de la base de datos con la tabla?
- 26. cómo obtener el último número de registro después de insertar el registro en la base de datos en el acceso
- 27. ¿Los cursores de la base de datos recogen los cambios en los datos subyacentes?
- 28. Cómo copiar datos de una base de datos/tabla a otra base de datos/tabla
- 29. Quiero copiar la tabla contenida en una base de datos e insertarla en otra tabla de base de datos
- 30. Registro de servidor en la base de datos o archivo de registro?
Sería tan fácil hacer esto desde la aplicación que realiza la llamada a la base de datos. Tendría el texto SQL y/o el nombre de sproc + los nombres y valores de los parámetros. Pero debe ser el desarrollador de la aplicación, y no el DBA, para tener control sobre eso. – DOK
Y también no registraría nada que realizara ninguna actualización/eliminación a través de otras conexiones. – Rory