Estou trabalhando em um projeto da web que envolve conteúdo editável pelo usuário e gostaria de poder acompanhar a versão do conteúdo real, que fica em um banco de dados. Basicamente, quero implementar históricos de alterações no estilo wiki.
Fazendo algumas pesquisas em segundo plano, vejo muita documentação sobre como versionar o esquema do banco de dados (o meu já está realmente controlado), mas qualquer estratégia existente sobre como rastrear as alterações no conteúdo do banco de dados é perdida na avalanche de material de versão do esquema, pelo menos nas minhas pesquisas.
Posso pensar em algumas maneiras de implementar meu próprio controle de alterações, mas todas elas parecem bastante grosseiras:
- Salve a linha inteira em cada alteração, relacione a linha de volta ao ID de origem com uma chave Primária (no que estou inclinado atualmente, é a mais simples). Muitas pequenas alterações podem produzir muita inchaço na mesa, no entanto.
- salve antes / depois / usuário / registro de data e hora para cada alteração, com um nome de coluna para relacionar a alteração novamente à coluna relevante.
- salve antes / depois / usuário / carimbo de data / hora com uma tabela para cada coluna (resultaria em muitas tabelas).
- salve diffs / user / timestamp para cada alteração com uma coluna (isso significa que você precisará percorrer todo o histórico de alterações para voltar a uma determinada data).
Qual é a melhor abordagem aqui? Rolar sozinho parece que provavelmente estou reinventando a (melhor) base de código de outra pessoa.
Pontos de bônus para o PostgreSQL.
fonte
Respostas:
A técnica que eu normalmente usei é salvar o registro completo, com um campo end_timestamp. Existe uma regra de negócios em que apenas uma linha pode ter um end_timestamp nulo, e esse é, naturalmente, o conteúdo ativo no momento.
Se você adotar esse sistema, recomendo fortemente que você adicione um índice ou restrição para aplicar a regra. Isso é fácil com o Oracle, pois um índice exclusivo pode conter um e apenas um nulo. Outros bancos de dados podem ser mais um problema. A aplicação da regra ao banco de dados manterá seu código honesto.
Você está certo de que muitas pequenas alterações criarão inchaço, mas é necessário trocar isso com o código e a simplicidade dos relatórios.
fonte
Observe que, se você usa o Microsoft SQL Server, já existe um recurso chamado Change Data Capture . Você ainda precisará escrever o código para acessar as revisões anteriores mais tarde (o CDC cria visualizações específicas para isso), mas pelo menos não precisa alterar o esquema de suas tabelas nem implementar o próprio rastreamento de alterações.
Sob o capô , o que acontece é que:
O CDC cria uma tabela adicional contendo as revisões,
Sua tabela original é usada como antes, ou seja, qualquer atualização é refletida diretamente nesta tabela,
A tabela CDC armazena apenas os valores alterados, o que significa que a duplicação de dados é mantida no mínimo.
O fato de as alterações serem armazenadas em uma tabela diferente tem duas consequências principais:
As seleções da tabela original são tão rápidas quanto sem CDC. Se bem me lembro, o CDC acontece após a atualização, portanto as atualizações são igualmente rápidas (embora não me lembre bem como o CDC gerencia a consistência dos dados).
Algumas alterações no esquema da tabela original levam à remoção do CDC. Por exemplo, se você adicionar uma coluna, o CDC não saberá como lidar com isso. Por outro lado, adicionar um índice ou restrição deve ser bom. Isso rapidamente se torna um problema se você ativar o CDC em uma tabela sujeita a alterações frequentes. Pode haver uma solução que permita alterar o esquema sem perder o CDC, mas não o procurei.
fonte
Resolva o problema "filosoficamente" e primeiro no código. E então "negocie" com código e banco de dados para que isso aconteça.
Como exemplo , se você estiver lidando com artigos genéricos, um conceito inicial para um artigo pode ser assim:
E no próximo nível mais básico, quero manter uma lista de revisões:
E posso me dar conta de que o corpo atual é apenas a revisão mais recente. E isso significa duas coisas: preciso que cada revisão seja datada ou numerada:
E ... e o corpo atual do artigo não precisa ser diferente da revisão mais recente:
Faltam alguns detalhes; mas ilustra que você provavelmente deseja duas entidades . Um representa o artigo (ou outro tipo de cabeçalho) e o outro é uma lista de revisões (agrupando quaisquer campos que façam sentido "filosófico" bom agrupar). Inicialmente, você não precisa de restrições especiais no banco de dados, porque seu código não se importa com nenhuma das revisões por si só - elas são propriedades de um artigo que conhece revisões.
Portanto, você não precisa se preocupar em sinalizar revisões de nenhuma maneira especial ou se apoiar em uma restrição de banco de dados para marcar o artigo "atual". Você apenas os marca um carimbo de data / hora (até mesmo um ID incluído automaticamente), torna-os relacionados ao artigo pai e deixa o artigo encarregado de saber que o "mais recente" é o mais relevante.
E você permite que um ORM lide com os detalhes menos filosóficos - ou oculte-os em uma classe de utilitário personalizada, se você não estiver usando um ORM pronto para uso.
Muito mais tarde, depois de fazer alguns testes de estresse, você pode pensar em tornar essa propriedade de revisão lenta ou com o atributo Body como carga lenta apenas na revisão mais alta. Mas, nesse caso, sua estrutura de dados não precisa ser alterada para acomodar essas otimizações.
fonte
Existe uma página wiki do PostgreSQL para um acionador de rastreamento de auditoria, que mostra como configurar um log de auditoria que fará o que você precisa.
Ele rastreia os dados originais completos de uma alteração, bem como a lista de novos valores para atualizações (para inserções e exclusões, há apenas um valor). Se você deseja restaurar uma versão antiga, pode pegar a cópia dos dados originais do registro de auditoria. Observe que, se seus dados envolverem chaves estrangeiras, esses registros também deverão ser revertidos para manter a consistência.
De um modo geral, se seu aplicativo de banco de dados passa a maior parte do tempo apenas com os dados atuais, acho melhor rastrear versões alternativas em uma tabela separada dos dados atuais. Isso manterá seus índices de tabela ativos mais gerenciáveis.
Se as linhas que você está rastreando são muito grandes e o espaço é uma preocupação séria, você pode tentar quebrar as alterações e armazenar diferenças / correções mínimas, mas é definitivamente mais trabalho para cobrir todos os tipos de tipos de dados. Já fiz isso antes e foi difícil reconstruir versões antigas de dados, percorrendo todas as alterações para trás, uma de cada vez.
fonte
Bem, acabei seguindo a opção mais simples, um gatilho que copia a versão antiga de uma linha para um log de histórico por tabela.
Se eu acabar com muito inchaço no banco de dados, posso analisar, possivelmente, algumas das pequenas alterações no histórico, se necessário.
A solução acabou sendo bastante confusa, pois eu queria gerar as funções de gatilho automaticamente. Eu sou SQLAlchemy, então pude produzir a tabela de histórico fazendo alguns hijinks de herança, o que foi bom, mas as funções de acionamento reais acabaram exigindo algumas alterações na string para gerar corretamente as funções do PostgreSQL e mapear as colunas de uma tabela para outro corretamente.
De qualquer forma, está tudo no github aqui .
fonte