Pergunta

Eu tenho sido encarregado de desenvolver uma solução que controla as alterações a um banco de dados.

Para atualizações eu preciso de captura:

  • data de actualização
  • valor antigo
  • novo valor
  • campo afetada
  • pessoa que faz a mudança
  • registro id
  • registro da tabela está em

Para exclusões:

  • data de exclusão
  • pessoa que faz delete
  • O título / descrição / id de registro excluído. As tabelas Estou rastreamento alterações em todos têm um campo de título ou descrição. Eu gostaria de capturar isso antes que o registro é excluído.
  • registro da tabela estava em

Para inserções:

  • data de inserção
  • pessoa que faz a mudança
  • registro id
  • registro da tabela está em

Eu pensei em algumas maneiras de fazer isso:

  • Eu estou usando procedimentos armazenados para quaisquer atualizações / exclusões / inserções. Gostaria de criar uma tabela genérica "tracking". Teria campos suficientes para capturar todos os dados. Eu, então, adicionar outra linha em cada proc armazenado para o efeito de "Inserir registro na tabela de rastreamento".
    • desvantagem: todas as atualizações / exclusões / inserções são todos misturados na mesma tabela
    • lotes de campos Nulled
    • Como posso controlar atualizações em lote / exclusões / inserções? <---- Isto pode não ser um problema. Eu realmente não fazer qualquer coisa como esta na aplicação.
    • como faço para capturar o usuário fazer a atualização. O banco de dados só vê uma conta.
    • editar um monte de código existente para editar.
  • Finalmente, eu poderia criar um gatilho que é chamado após atualizações / exclusões / inserções. Muitas das mesmas desvantagens como a primeira solução, exceto: eu teria que editar o máximo de código. Eu não sei como eu iria acompanhar as atualizações. Não parece que há uma maneira usando gatilhos para ver os registros atualizados recentemente.

Eu estou usando asp.net, C #, sql server 2005, iis6, janelas de 2003. Eu não tenho orçamento para que infelizmente eu não posso comprar nada para me ajudar com isso.

Obrigado por suas respostas!

Foi útil?

Solução

Um gatilho não teria todas as informações que você precisa para um monte de razões -. Mas sem identificação do usuário é o argumento decisivo

Eu diria que você está no caminho certo com um sp comum para inserir sempre que uma alteração é feita. Se você é padronizar em sp é para suas interfaces então você está à frente do jogo -., Será difícil a esgueirar-se em uma mudança que não é controlado

olhar para isto como o equivalente a uma trilha de auditoria em uma aplicação de contabilidade - este é o Journal - uma única tabela com cada transação registrada. Eles não iriam implementar revistas separadas para depósitos, saques, ajustes, etc., e este é o mesmo princípio.

Outras dicas

Eu odeio a lado passo a questão e eu sei que você não tem orçamento, mas a solução mais simples será para atualizar para o SQL Server 2008. Ele tem esse recurso construído em . Eu pensei que deveria pelo menos ser mencionado para qualquer pessoa que se depara com esta questão, mesmo se você não pode usá-lo você mesmo.

(Entre as edições implementáveis ??de SQL 2008, este recurso está disponível apenas no Enterprise.)

Gostaria de sugerir que você use 2 da coluna em cada mesa. nomes rowhistory e IsDeleted eo tipo de dados será xml e pouco. Nunca excluir as linhas, use sempre a bandeira IsDeleted Agora vá com gatilhos de atualização. Vou dar-lhe exemplo para o mesmo Eu tenho esta tabela chamada 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]

Agora, depois de criar a mesa tudo o que você precisa fazer é copiar e colar o código abaixo e sua tarefa é feito para a tabela da página. Ele vai começar a gravar a história da linha na mesma linha que é atualizado juntamente com os valores antigos e novos.

                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  

Agora, sempre que você vai executar qualquer atualização seus dados serão armazenados em rowhistory coluna

Uma maneira que eu vi este tratado (embora eu não recomendo, honestamente) é lidar com isso através de procedimentos armazenados, passando o ID do usuário / nome de usuário / whatever como parâmetro. Os procedimentos armazenados iria chamar um procedimento de registro, que escreveu os detalhes relevantes em uma tabela log central.

Aqui é onde ele ficou um whacky pouco, embora ...

Para inserções / atualizações, a linha (s) relevantes foram armazenadas na tabela como dados XML uma vez que o INSERT / UPDATE tinha concluído com êxito. Para DELETEs, a linha foi armazenada antes da execução EXCLUIR (embora, realisticamente, eles poderiam ter começado a partir da saída da instrução DELETE - pelo menos com o SQL Server 2005).

Se bem me lembro, a tabela só tinha um par de colunas: UserID, DateTime da extração de madeira, tipo de transação (I / U / D), dados XML contendo os registros relevantes, nome da tabela, e valor da chave primária (usado principalmente para busca rápida do que registra eles queriam).

Muitas maneiras de esfolar um gato, embora ...

O meu conselho é para manter é simples. Expandi-lo mais tarde, se / quando você precisar.

Se você tem a capacidade de fazê-lo, bloqueio para baixo os usuários só será capaz de executar declarações acionáveis ??em tabelas através de procedimentos armazenados e depois lidar com o registro (como quiser) de lá.

nós construímos nossa própria e só precisava do usuário e pc passado para cada procedimento armazenado add / update. então é apenas uma questão de conseguir o registro original adn preencher as variáveis ??e comparando-as com o passado em variáveis ??e registrar os dados para a nossa mesa. para exclusões só temos uma cópia do mesas originários + um campo timestamp para que o registro não é realmente apagado e pode ser restaurada a qualquer hora que precisamos (obviamente as verificações de rotina de exclusão para os relacionamentos FK e tal).

Adicionar / atualizar a tabela log olhares como data hora, Nome da tabela, nome da coluna, record_id, old_value, novo valor, ID do usuário, computador

nós nunca inserir valores nulos para que convertê-los para esvaziar cordas, novas entradas são marcadas com '{nova entrada}' na coluna old_value. record_id é composta de tantas colunas-chave para identificar exclusivamente esse único registro (field1 + '' + field2 + ...)

Em primeiro lugar, em todas as suas tabelas você deve ter pelo menos estas colunas adicionadas às colunas de dados DateCreated, UserCreated, DateModified, UserModified. Possivelmente você pode querer adicionar um "Estado" ou coluna "LastAction" de modo que você nunca realmente excluir uma linha que você acabou de configurá-lo para um status deletadas / insweridas / atualizado. Em seguida, você poderia criar uma "tabela de História", que é uma cópia exata da primeira tabela. Então, em quaisquer atualizações ou exclusões têm o gatilho copiar as entradas tabela excluída na tabela de História mudar o DateModified, UserModified, e campos de status ao mesmo tempo.

Eu tive uma configuração no SQL Server onde iríamos usar exibições para acessar nossos dados, que iria lidar com inserções, atualizações e exclusões com gatilhos INSTEAD OF.

Por exemplo:. Um em vez de excluir gatilho na vista marcaria os registros na tabela subjacente como excluído, ea vista foi filtrado para registros não mostram excluídos

Em todos os gatilhos, atualizamos a data de modificação e nome de usuário. O problema é que que registra o nome de usuário de banco de dados, o que não é o mesmo que o nome de usuário do aplicativo final.

As necessidades vista ser obrigado esquema para que isso funcione.

Sobre usuários madeireiras que muda DB: Você pode criar tantos usuários de SQL que você precisa para o seu DB e se você usar sessões e acesso restrito / registado para o seu programa / script você pode usar essa informação para iniciar as configurações de conexão diferente DB (ou seja, nome de usuário), antes de qualquer operação com DB.

Pelo menos isso deve ser factível para scripts PHP-sábio, mas eu poderia ser errado para asp.net.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top