2009-05-22 12 views
8

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

+0

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

+2

Y también no registraría nada que realizara ninguna actualización/eliminación a través de otras conexiones. – Rory

Respuesta

1

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.

+0

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

+1

¿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

+0

Finalmente, convenció a los DBA para que activen el Analizador de SQL. Sin embargo, solo se enciende durante una semana. – Jason

5

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.

+0

+1 Interesante, aunque hacer esto en un gatillo se siente un poco equivocado – Andomar

+0

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

+0

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

1

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).

2

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).

4

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.

+1

+1 Alguien en el trabajo se metió en una gran disputa con un cliente. Él dijo "sin registro, no hay problema". Resultó que estaba registrando de una transacción ... – Andomar

+0

Yepp, he sido ese tipo :) – idstam

1

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.

+1

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

-4

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).

+3

¡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

7

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 
+0

¿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 –

+0

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

2

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.

+0

Es probable que se elimine un enlace sin resumen de contenido, especialmente si el enlace deja de funcionar. –

1

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.

0

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.

Cuestiones relacionadas