Domanda

Mi è stato assegnato il compito di sviluppare una soluzione che tenga traccia delle modifiche a un database.

Per gli aggiornamenti che devo acquisire:

  • data di aggiornamento
  • vecchio valore
  • nuovo valore
  • campo interessato
  • persona che sta cambiando
  • ID record
  • il record della tabella è in

Per le eliminazioni:

  • data di eliminazione
  • persona che cancella
  • Titolo / descrizione / ID del record eliminati. Le tabelle su cui sto monitorando le modifiche hanno tutte un titolo o un campo di descrizione. Vorrei catturarlo prima che il record fosse cancellato.
  • il record della tabella era in

Per inserti:

  • data di inserimento
  • persona che sta cambiando
  • ID record
  • il record della tabella è in

Ho pensato ad alcuni modi per farlo:

  • Sto utilizzando le procedure memorizzate per eventuali aggiornamenti / eliminazioni / inserti. Vorrei creare un generico & Quot; tracking & Quot; tavolo. Avrebbe abbastanza campi per acquisire tutti i dati. Vorrei quindi aggiungere un'altra riga in ogni proc memorizzato all'effetto di & Quot; Inserire il record nella tabella di tracciamento & Quot ;.
    • svantaggio: tutti gli aggiornamenti / eliminazioni / inserti sono tutti confusi nella stessa tabella
    • molti campi NULLed
    • come posso tenere traccia degli aggiornamenti / eliminazioni / inserimenti batch? < ---- questo potrebbe non essere un problema. In realtà non faccio nulla del genere nell'applicazione.
    • come posso catturare l'utente mentre aggiorna. Il database vede solo un account.
    • modifica molto codice esistente da modificare.
  • Infine, potrei creare un trigger che viene chiamato dopo aggiornamenti / eliminazioni / inserimenti. Molti degli stessi aspetti negativi della prima soluzione, tranne: avrei dovuto modificare tanto codice. Non sono sicuro di come tracciare gli aggiornamenti. Non sembra che ci sia un modo per usare i trigger per vedere i record aggiornati di recente.

Sto usando asp.net, C #, sql server 2005, iis6, windows 2003. Purtroppo non ho un budget così triste che non posso comprare nulla per aiutarmi in questo.

Grazie per le tue risposte!

È stato utile?

Soluzione

Un trigger non avrebbe tutte le informazioni necessarie per una serie di motivi, ma nessun ID utente è il copertoncino.

Direi che sei sulla buona strada con uno sp comune da inserire ovunque venga apportata una modifica. Se stai standardizzando su sp per le tue interfacce, allora sei avanti al gioco - sarà difficile intrufolarsi in una modifica che non viene tracciata.

Guarda questo come l'equivalente di una pista di controllo in un'applicazione di contabilità - questo è il Journal - una singola tabella con ogni transazione registrata. Non implementerebbero riviste separate per depositi, prelievi, rettifiche, ecc. E questo è lo stesso principio.

Altri suggerimenti

Odio affrontare il problema e so che non hai budget, ma la soluzione più semplice sarà l'aggiornamento a SQL Server 2008. Ha questa funzionalità incorporato . Ho pensato che dovrebbe essere menzionato almeno per chiunque incontri questa domanda, anche se non puoi usarla da solo.

(Tra le edizioni distribuibili di SQL 2008, questa funzionalità è disponibile solo in Enterprise.)

Ti suggerirei di usare 2 colonne in ogni tabella. nomi storia della riga e IsDeleted e il tipo di dati sarà xml e bit. Non eliminare mai le righe, usa sempre il flag IsDeleted Ora vai con i trigger di aggiornamento. Ti darò un esempio per lo stesso Ho questa tabella chiamata Pagina

    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]

Ora, dopo aver creato la tabella, tutto ciò che devi fare è copiare e incollare il codice qui sotto e il tuo compito è fatto per la tabella Pagina. Inizierà a registrare la cronologia della riga nella stessa riga che viene aggiornata insieme ai valori vecchi e nuovi.

                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  

Ora, ogni volta che eseguirai qualsiasi aggiornamento, i tuoi dati verranno archiviati nella colonna storia delle righe

Un modo in cui l'ho visto gestito (anche se non lo consiglierei, onestamente) è gestirlo tramite procedure memorizzate, passando userid / username / qualunque come parametro. Le procedure memorizzate chiamerebbero una procedura di registrazione, che ha scritto i dettagli rilevanti in una tabella di registro centrale.

Ecco dove è diventato un po 'strano, però ...

Per INSERT / UPDATE, le righe pertinenti sono state memorizzate nella tabella come dati XML dopo che INSERT / UPDATE sono stati completati correttamente. Per DELETE, la riga è stata archiviata prima dell'esecuzione di DELETE (sebbene, realisticamente, avrebbero potuto ottenerla dall'output dell'istruzione DELETE - almeno con SQL Server 2005).

Se ricordo bene, la tabella aveva solo un paio di colonne: UserID, DateTime della registrazione, Tipo di transazione (I / U / D), dati XML contenenti le righe pertinenti, nome della tabella e valore della chiave primaria (usato principalmente per una rapida ricerca dei record desiderati).

Molti modi per scuoiare un gatto, però ...

Il mio consiglio è di mantenerlo semplice. Espandilo in un secondo momento se / quando è necessario.

Se hai la possibilità di farlo, blocca gli utenti per poter eseguire solo istruzioni eseguibili sui tavoli tramite procedure memorizzate e quindi gestire la registrazione (come preferisci) da lì.

abbiamo creato il nostro e avevamo solo bisogno che l'utente e il PC passassero in ogni procedura di aggiunta / aggiornamento memorizzata. quindi si tratta solo di ottenere il record originale e di popolare le variabili, confrontarle con le variabili passate e registrare i dati nella nostra tabella. per le eliminazioni abbiamo solo una copia delle tabelle di origine + un campo timestamp in modo che il record non sia mai realmente cancellato e possa essere ripristinato in qualsiasi momento necessario (ovviamente i controlli di routine di eliminazione per le relazioni FK e simili).

assomiglia / aggiunge la tabella di registro appuntamento, table_name, column_name, record_id, old_value, NEW_VALUE, ID utente, del computer

non inseriamo mai valori null quindi li convertiamo in stringhe vuote, le nuove voci sono contrassegnate con '{new entry}' nella colonna old_value. record_id è composto da altrettante colonne chiave per identificare in modo univoco quel singolo record (field1 + '.' + field2 + ...)

Prima di tutto, in tutte le tabelle dovresti avere almeno queste colonne aggiunte alle colonne di dati DateCreated, UserCreated, DateModified, UserModified. Forse potresti voler aggiungere un & Quot; Status & Quot; oppure " LastAction " colonna in modo da non eliminare mai una riga, ma semplicemente impostarla su uno stato eliminato / inserito / aggiornato. Successivamente è possibile creare un & Quot; Tabella cronologica & Quot; che è una copia esatta della prima tabella. Quindi su eventuali aggiornamenti o eliminazioni, il trigger deve copiare le voci della tabella Eliminate nella tabella Cronologia modificando contemporaneamente i campi DateModified, UserModified e Status.

Ho avuto una configurazione in SQL Server in cui avremmo usato le viste per accedere ai nostri dati, che avrebbe gestito inserimenti, aggiornamenti ed eliminazioni con i trigger INSTEAD OF.

Ad esempio: un INSTEAD OF DELETE sulla vista contrassegnerebbe i record nella tabella sottostante come eliminati e la vista veniva filtrata per non mostrare i record eliminati.

In tutti i trigger, abbiamo aggiornato una data di modifica e un nome utente. Il problema è che registra il nome utente del database, che non è lo stesso del nome utente dell'applicazione finale.

La vista deve essere associata allo schema affinché funzioni.

Informazioni sulla registrazione degli utenti che cambiano DB: puoi creare tutti gli utenti SQL di cui hai bisogno per il tuo DB e se usi sessioni e accesso limitato / registrato al tuo programma / script puoi utilizzare tali informazioni per avviare impostazioni di connessione DB diverse (ad esempio nome utente), prima di qualsiasi operazione con DB.

Almeno questo dovrebbe essere fattibile per gli script PHP-saggi, ma potrei essere sbagliato per asp.net.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top