Pregunta

Me encargaron desarrollar una solución que rastrea los cambios en una base de datos.

Para las actualizaciones que necesito capturar:

  • fecha de actualización
  • valor anterior
  • nuevo valor
  • campo afectado
  • persona haciendo cambio
  • ID de registro
  • el registro de la tabla está en

Para eliminaciones:

  • fecha de eliminación
  • persona haciendo borrar
  • El título / descripción / id del registro eliminado. Las tablas en las que estoy rastreando los cambios tienen un campo de título o descripción. Me gustaría capturar esto antes de que se elimine el registro.
  • el registro de la tabla estaba en

Para inserciones:

  • fecha de inserción
  • persona haciendo cambio
  • ID de registro
  • el registro de la tabla está en

He pensado en algunas formas de hacer esto:

  • Estoy usando procedimientos almacenados para cualquier actualización / eliminación / inserción. Crearía un & Quot genérico; seguimiento & Quot; mesa. Tendría suficientes campos para capturar todos los datos. Luego agregaría otra línea en cada proceso almacenado en el efecto de & Quot; Insertar registro en la tabla de seguimiento & Quot ;.
    • inconveniente: todas las actualizaciones / eliminaciones / inserciones están todas mezcladas en la misma tabla
    • muchos campos NULADOS
    • ¿cómo puedo rastrear actualizaciones / eliminaciones / insertos por lotes? < ---- esto podría no ser un problema. Realmente no hago nada como esto en la aplicación.
    • ¿Cómo capturo al usuario que realiza la actualización? La base de datos solo ve una cuenta.
    • edite una gran cantidad de código existente para editar.
  • Por último, podría crear un activador que se llama después de actualizaciones / eliminaciones / inserciones. Muchas de las mismas desventajas que la primera solución, excepto: tendría que editar tanto código. No estoy seguro de cómo rastrear las actualizaciones. No parece que haya una forma de utilizar los disparadores para ver los registros actualizados recientemente.

Estoy usando asp.net, C #, sql server 2005, iis6, windows 2003. No tengo presupuesto, así que lamentablemente no puedo comprar nada que me ayude con esto.

¡Gracias por sus respuestas!

¿Fue útil?

Solución

Un disparador no tendría toda la información que necesita por varias razones, pero ninguna identificación de usuario es el factor decisivo.

Yo diría que estás en el camino correcto con un sp común para insertar donde sea que se haga un cambio. Si está estandarizando los sp para sus interfaces, entonces está por delante del juego: será difícil infiltrarse en un cambio que no se rastrea.

Mire esto como el equivalente de una pista de auditoría en una aplicación de contabilidad, este es el Diario, una sola tabla con cada transacción registrada. No implementarían diarios separados para depósitos, retiros, ajustes, etc. y este es el mismo principio.

Otros consejos

Odio eludir el problema y sé que no tiene presupuesto, pero la solución más simple será actualizar a SQL Server 2008. Tiene esta característica integrado en . Pensé que al menos debería mencionarse para cualquier otra persona que se encuentre con esta pregunta, incluso si no puede usarla usted mismo.

(Entre las ediciones desplegables de SQL 2008, esta característica solo está disponible en Enterprise).

Te sugiero que uses 2 columnas en cada tabla. nombra rowhistory e IsDeleted y el tipo de datos será xml y bit. Nunca elimine las filas, utilice siempre el indicador IsDeleted Ahora ve con disparadores de actualización. Te daré ejemplo para lo mismo Tengo esta tabla llamada Página

    CREATE TABLE te_Page([Id] [int] IDENTITY(1,1) NOT NULL, [Name] [varchar](200) NOT NULL, [Description] [varchar](200) NULL,[CreatedBy] [uniqueidentifier] NULL, [CreatedDate] [datetime] NOT NULL, [UpdatedBy] [uniqueidentifier] NULL, [UpdatedDate] [datetime] NULL, [IsDeleted] [bit] NULL, [RowHistory] [xml] NULL, CONSTRAINT [PK_tm_Page] PRIMARY KEY CLUSTERED ([Id] ASC )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY] ) ON [PRIMARY]

Ahora, después de crear la tabla, todo lo que necesita hacer es copiar y pegar el siguiente código y su tarea estará lista para la tabla de páginas. Comenzará a registrar el historial de la fila en la misma fila que se actualiza junto con los valores antiguos y nuevos.

                ALTER Trigger [dbo].[Trg_Te_Page]    
        On [dbo].[te_Page]                
        After Update                
        As                 
        --If @@rowcount = 0 Or Update(RowHistory)    
        --Return    

        Declare @xml NVARCHAR(MAX)     
        Declare @currentxml NVARCHAR(MAX)   
        Declare @node NVARCHAR(MAX)    
        Declare @ishistoryexists XML    

        Declare @FormLineAttributeValueId int  

        -- new Values  
        Declare @new_Name varchar(200)  
        Declare @new_Description varchar(200)  

        Declare @new_CreatedBy UNIQUEIDENTIFIER    
        Declare @new_CreatedDate DATETIME    
        Declare @new_UpdatedBy UNIQUEIDENTIFIER    
        Declare @new_UpdatedDate DATETIME    
        Declare @new_IsDeleted BIT  

        --old values  
        Declare @old_Name varchar(200)  
        Declare @old_Description varchar(200)  

        Declare @old_CreatedBy UNIQUEIDENTIFIER    
        Declare @old_CreatedDate DATETIME    
        Declare @old_UpdatedBy UNIQUEIDENTIFIER    
        Declare @old_UpdatedDate DATETIME    
        Declare @old_IsDeleted BIT  


        -- declare temp fmId  
        Declare @fmId int  
        -- declare cursor  
        DECLARE curFormId cursor   
        FOR select Id from INSERTED   
        -- open cursor       
        OPEN curFormId  
        -- fetch row  
        FETCH NEXT FROM curFormId INTO @fmId  

        WHILE @@FETCH_STATUS  = 0   
        BEGIN   

        Select   
        @FormLineAttributeValueId = Id,   
        @old_Name = Name,  
        @old_Description = [Description],  

        @old_CreatedBy = CreatedBy,    
        @old_CreatedDate =CreatedDate,  
        @old_UpdatedBy =UpdatedBy,    
        @old_UpdatedDate =UpdatedDate,  
        @old_IsDeleted  = IsDeleted,  
        @currentxml = cast(RowHistory as NVARCHAR(MAX))  
        From DELETED where Id=@fmId  



        Select      
        @new_Name = Name,  
        @new_Description = [Description],  

        @new_CreatedBy = CreatedBy,    
        @new_CreatedDate =CreatedDate,  
        @new_UpdatedBy =UpdatedBy,    
        @new_UpdatedDate =UpdatedDate,  
        @new_IsDeleted  = IsDeleted  
        From INSERTED where Id=@fmId  

        set @old_Name = Replace(@old_Name,'&','&amp;')
        set @old_Name = Replace(@old_Name,'>','&gt;')  
        set @old_Name = Replace(@old_Name,'<','&lt;')     
        set @old_Name = Replace(@old_Name,'"','&quot;')
        set @old_Name = Replace(@old_Name,'''','&apos;')          

        set @new_Name = Replace(@new_Name,'&','&amp;')      
        set @new_Name = Replace(@new_Name,'>','&gt;')  
        set @new_Name = Replace(@new_Name,'<','&lt;')     
        set @new_Name = Replace(@new_Name,'"','&quot;')
        set @new_Name = Replace(@new_Name,'''','&apos;') 

        set @old_Description = Replace(@old_Description,'&','&amp;')
        set @old_Description = Replace(@old_Description,'>','&gt;')  
        set @old_Description = Replace(@old_Description,'<','&lt;')     
        set @old_Description = Replace(@old_Description,'"','&quot;')
        set @old_Description = Replace(@old_Description,'''','&apos;')          

        set @new_Description = Replace(@new_Description,'&','&amp;')      
        set @new_Description = Replace(@new_Description,'>','&gt;')  
        set @new_Description = Replace(@new_Description,'<','&lt;')     
        set @new_Description = Replace(@new_Description,'"','&quot;')
        set @new_Description = Replace(@new_Description,'''','&apos;')   

        set @xml = ''     

        BEGIN      

        -- for Name  
        If ltrim(rtrim(IsNull(@new_Name,''))) != ltrim(rtrim(IsNull(@old_Name,'')))    
        set @xml = @xml + '<ColumnInfo ColumnName="Name" OldValue="'+ @old_Name + '" NewValue="' + @new_Name + '"/>'    

        -- for Description  
        If ltrim(rtrim(IsNull(@new_Description,''))) != ltrim(rtrim(IsNull(@old_Description,'')))    
        set @xml = @xml + '<ColumnInfo ColumnName="Description" OldValue="'+ @old_Description + '" NewValue="' + @new_Description + '"/>'    

        -- CreatedDate     
        If IsNull(@new_CreatedDate,'') != IsNull(@old_CreatedDate,'')  
        set @xml = @xml + '<ColumnInfo ColumnName="CreatedDate" OldValue="'+ cast(isnull(@old_CreatedDate,'') as varchar(100)) + '" NewValue="' + cast(isnull(@new_CreatedDate,'') as varchar(100)) + '"/>'    

        -- CreatedBy     
        If cast(IsNull(@new_CreatedBy,'00000000-0000-0000-0000-000000000000')as varchar (36)) != cast(IsNull(@old_CreatedBy,'00000000-0000-0000-0000-000000000000')as varchar(36))    
        set @xml = @xml + '<ColumnInfo ColumnName="CreatedBy" OldValue="'+ cast(IsNull(@old_CreatedBy,'00000000-0000-0000-0000-000000000000') as varchar(36)) + '" NewValue="' + cast(isnull(@new_CreatedBy,'00000000-0000-0000-0000-000000000000') as varchar(36))+
        '"/>'    

        -- UpdatedDate       
        If IsNull(@new_UpdatedDate,'') != IsNull(@old_UpdatedDate,'')    
        set @xml = @xml + '<ColumnInfo ColumnName="UpdatedDate" OldValue="'+ cast(IsNull(@old_UpdatedDate,'') as varchar(100)) + '" NewValue="' + cast(IsNull(@new_UpdatedDate,'') as varchar(100)) + '"/>'    

        -- UpdatedBy     
        If cast(IsNull(@new_UpdatedBy,'00000000-0000-0000-0000-000000000000') as varchar(36)) != cast(IsNull(@old_UpdatedBy,'00000000-0000-0000-0000-000000000000') as varchar(36))    
        set @xml = @xml + '<ColumnInfo ColumnName="UpdatedBy" OldValue="'+ cast(IsNull(@old_UpdatedBy,'00000000-0000-0000-0000-000000000000') as varchar(36)) + '" NewValue="' + cast(IsNull(@new_UpdatedBy,'00000000-0000-0000-0000-000000000000') as varchar(36))+
        '"/>'    

        -- IsDeleted  
        If cast(IsNull(@new_IsDeleted,'') as varchar(10)) != cast(IsNull(@old_IsDeleted,'') as varchar(10))    
        set @xml = @xml + '<ColumnInfo ColumnName="IsDeleted" OldValue="'+ cast(IsNull(@old_IsDeleted,'') as varchar(10)) + '" NewValue="' + cast(IsNull(@new_IsDeleted,'') as varchar(10)) + '" />'    

        END    

        Set @xml = '<RowInfo TableName="te_Page" UpdatedBy="' + cast(IsNull(@new_UpdatedBy,'00000000-0000-0000-0000-000000000000') as varchar(50)) +  '" UpdatedDate="' + Convert(Varchar(20),GetDate()) + '">' + @xml + '</RowInfo>'    
        Select @ishistoryexists = RowHistory From DELETED     

        --print @ishistoryexists  


        If @ishistoryexists is null    
        Begin     
        Set @xml = '<History>' + @xml + '</History>'      
        Update te_Page    
        Set    
        RowHistory = @xml    
        Where     
        Id = @FormLineAttributeValueId    

        End    

        Else    
        Begin     
        set @xml = REPLACE(@currentxml, '<History>', '<History>' + @xml)  
        Update te_Page  
        Set  
        RowHistory = @xml  
        Where   
        Id = @FormLineAttributeValueId     
        End  


        FETCH NEXT FROM curFormId INTO @fmId  
        END   


        CLOSE curFormId  
        DEALLOCATE curFormId  

Ahora, cada vez que realice una actualización, sus datos se almacenarán en la filahistoria columna

Una forma en que he visto esto manejado (aunque sinceramente no lo recomendaría) es manejarlo a través de procedimientos almacenados, pasando el ID de usuario / nombre de usuario / lo que sea como parámetro. Los procedimientos almacenados llamarían un procedimiento de registro, que escribió los detalles relevantes en una tabla de registro central.

Aquí es donde se puso un poco loco, aunque ...

Para INSERT / UPDATE, las filas relevantes se almacenaron en la tabla como datos XML una vez que INSERT / UPDATE se completó con éxito. Para DELETEs, la fila se almacenó antes de la ejecución de DELETE (aunque, de manera realista, podrían haberla obtenido de la salida de la declaración DELETE, al menos con SQL Server 2005).

Si no recuerdo mal, la tabla solo tenía un par de columnas: UserID, DateTime of the logging, Transaction Type (I / U / D), datos XML que contienen las filas relevantes, el nombre de la tabla y el valor de la clave primaria (utilizado principalmente para una búsqueda rápida de los registros que querían).

Muchas formas de pelar un gato, sin embargo ...

Mi consejo es que mantener es simple. Expanda más adelante si / cuando lo necesite.

Si tiene la capacidad de hacerlo, bloquee a los usuarios para que solo puedan realizar declaraciones procesables en las tablas a través de procedimientos almacenados y luego maneje el registro (como quiera) a partir de allí.

creamos el nuestro y solo necesitábamos que el usuario y la PC pasaran a cada procedimiento almacenado de agregar / actualizar. entonces es solo una cuestión de obtener el registro original y llenar las variables y compararlas con las variables pasadas y registrar los datos en nuestra tabla. para las eliminaciones, solo tenemos una copia de las tablas de origen + un campo de marca de tiempo para que el registro nunca se elimine realmente y se pueda restaurar en cualquier momento que lo necesitemos (obviamente, la rutina de eliminación verifica las relaciones FK y demás).

agregar / actualizar la tabla de registro se ve así fecha y hora, nombre de la tabla, nombre_columna, record_id, valor antiguo, nuevo valor, user_id, computadora

nunca insertamos nulos, por lo que los convertimos en cadenas vacías, las nuevas entradas están marcadas con '{nueva entrada}' en la columna old_value. record_id está formado por tantas columnas clave para identificar de forma única ese registro único (campo1 + '.' + campo2 + ...)

En primer lugar, en todas sus tablas debe tener al menos estas columnas agregadas a las columnas de datos DateCreated, UserCreated, DateModified, UserModified. Posiblemente desee agregar un & Quot; Estado & Quot; o " LastAction " columna para que nunca elimine una fila, simplemente configúrela en un estado eliminado / insertado / actualizado. A continuación, puede crear una & Quot; Tabla de historial & Quot; que es una copia exacta de la primera tabla. Luego, en cualquier actualización o eliminación, haga que el activador copie las entradas de la tabla Eliminada en la tabla Historial cambiando los campos DateModified, UserModified y Status al mismo tiempo.

He tenido una configuración en SQL Server donde usaríamos vistas para acceder a nuestros datos, lo que manejaría las inserciones, actualizaciones y eliminaciones con desencadenadores INSTEAD OF.

Por ejemplo: un disparador INSTEAD OF DELETE en la vista marcaría los registros en la tabla subyacente como eliminados, y la vista se filtró para no mostrar registros eliminados.

En todos los disparadores, actualizamos una fecha de modificación y un nombre de usuario. El problema es que registra el nombre de usuario de la base de datos, que no es lo mismo que el nombre de usuario final de la aplicación.

La vista debe estar vinculada al esquema para que esto funcione.

Acerca del registro de usuarios que cambian la base de datos: puede crear tantos usuarios de SQL como necesite para su base de datos y si usa sesiones y acceso restringido / registrado a su programa / script puede usar esa información para iniciar diferentes configuraciones de conexión de base de datos (es decir, nombre de usuario), antes de cualquier operación con DB.

Al menos eso debería ser factible para scripts PHP-sabios, pero podría estar equivocado para asp.net.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top