Eu gostaria de implementar um recurso "anular exclusão" em um aplicativo Web, para que um usuário possa mudar de idéia e recuperar um registro excluído. Pensamentos sobre como implementar isso? Algumas opções que considerei estão na verdade excluindo o registro em questão e armazenando as alterações em uma tabela de auditoria separada ou não excluindo o registro e usando uma coluna booleana "excluída" para marcá-la como excluída. A última solução exigiria lógica de aplicativo adicional para ignorar os registros "excluídos" em circunstâncias normais, mas facilitaria muito a implementação da recuperação dos registros no lado do aplicativo.
database-design
delete
audit
Abie
fonte
fonte
Respostas:
Sim, eu definitivamente optaria pela segunda opção, mas acrescentaria mais um campo por campo de data.
Então você adiciona:
Isso permitiria que você desse um tempo para a ação de exclusão.
Se o tempo for inferior a uma hora, é possível cancelar a exclusão.
Para realmente excluir a entrada excluída, basta criar um procedimento armazenado que limpará todas as entradas com a exclusão definida como true e o tempo maior que uma hora e colocá-lo como uma guia cron que é executada a cada 24 horas
A hora é apenas um exemplo.
fonte
cleaned
ou algo assim - que indica que os dados associados a esse registro foram excluídos de forma adequada e abrangente. O registro pode ser cancelado, a menos quecleaned
seja verdadeiro, caso em que é irrecuperável.deleted_at
contendo a semântica dodelete
booleano e odelete_date
carimbo de data e hora. Sedeleted_at
isNULL
handle, o casodelete
éFALSE
edelete_date
éNULL
,deleted_at
contendo um registro de data e hora, o casodelete
éTRUE
edelete_date
contém um registro de data e hora, economizando tempo, armazenamento e lógica de aplicativo.deleted
campo pode melhorar muito o desempenho quando você está consultando linhas não eliminadasEm nossos aplicativos, na verdade, não excluímos nada a pedido dos usuários (nossos clientes estão em ambientes regulamentados onde a exclusão de qualquer coisa pode potencialmente levar a problemas legais).
Mantemos as versões mais antigas em uma tabela de auditoria separada (portanto, para a tabela some_table, onde também é uma tabela chamada some_table_audit), que é idêntica, além de ter um identificador de versão adicional (um carimbo de data e hora se o seu banco de dados suportar valores de tempo suficientemente granulares, um número de versão inteiro) ou UUID, que é uma chave estrangeira para uma tabela de auditoria geral, etc.) e atualize a tabela de auditoria automaticamente por acionador (para que não seja necessário tornar todo o código que atualiza os registros cientes dos requisitos de auditoria).
Deste jeito:
Se estiver usando um registro de data e hora em vez de (ou também) um número de versão inteiro, você pode usá-lo para excluir as cópias mais antigas após um período de tempo definido, se necessário. Mas o espaço em disco é relativamente barato atualmente, portanto, a menos que tenhamos motivos para descartar dados antigos (ou seja, regulamentos de proteção de dados que dizem que você deve excluir dados do cliente após X meses / anos), não o faríamos.
Essa resposta existe há alguns anos e algumas coisas importantes que podem afetar esse tipo de planejamento foram alteradas desde então. Não vou entrar em detalhes maciços, mas com orgulho para as pessoas que estão lendo isso hoje:
O SQL Server 2016 introduziu "tabelas temporais com versão do sistema", que executam grande parte desse trabalho para você e, além disso, como um bom açúcar sintático é fornecido para facilitar a construção e a manutenção de consultas históricas, e coordenam um subconjunto de alterações de esquema entre os tabelas de base e histórico. Eles não estão isentos de advertências, mas são uma ferramenta poderosa para esse tipo de objetivo. Recursos semelhantes também estão disponíveis em outros sistemas de banco de dados.
Alterações na legislação de proteção de dados, principalmente a introdução do RGPD, podem alterar significativamente a questão de quando os dados devem ser excluídos. Você deve ponderar o saldo de não excluir dados que possam ser úteis (ou, de fato, legalmente exigidos) para fins de auditoria em uma data posterior, contra a necessidade de respeitar os direitos das pessoas (de maneira geral e especificamente especificada na legislação relevante) ao considerar seus projetos. Isso pode ser um problema nas tabelas temporais com versão do sistema, pois você não pode modificar o histórico para limpar dados pessoais sem alterações de curto prazo no esquema para desativar o rastreamento do histórico enquanto faz alterações.
fonte
Com uma coluna excluída booleana, você começará a ter problemas se sua tabela começar a crescer e ficar realmente grande. Sugiro que você mova as colunas excluídas uma vez por semana (mais ou menos, dependendo de suas especificações) para uma tabela diferente. Dessa forma, você tem uma boa e pequena tabela ativa e uma grande, contendo todos os registros coletados ao longo do tempo.
fonte
Eu iria com a mesa separada. O Ruby on Rails possui um
acts_as_versioned
plug - in, que basicamente salva uma linha em outra tabela com o postfix_version
antes de atualizá -lo. Embora você não precise desse comportamento exato, ele também deve funcionar no seu caso (copiar antes de excluir).Como o @Spredzy, eu também recomendo adicionar uma
delete_date
coluna para poder limpar programaticamente os registros que não foram restaurados após X horas / dias / qualquer coisa.fonte
A solução que usamos internamente para esse assunto é ter uma coluna de status com alguns valores codificados para alguns estados específicos do objeto: Excluídos, Ativos, Inativos, Abertos, Fechados, Bloqueados - cada status com algum significado usado no aplicativo. Do ponto de vista do banco de dados, não removemos objetos, apenas alteramos o status e mantemos o histórico de cada alteração na tabela de objetos.
fonte
Quando você diz que "A última solução exigiria uma lógica de aplicativo adicional para ignorar os registros 'excluídos'", a solução simples é ter uma exibição que os filtre.
fonte
Semelhante ao sugerido pela Spredzy, usamos um campo de carimbo de data / hora para exclusão em todos os nossos aplicativos. O booleano é supérfluo, pois o registro de data e hora está sendo definido indica que o registro foi excluído. Dessa forma, nosso PDO sempre adiciona
AND (deleted IS NULL OR deleted = 0)
às instruções de seleção, a menos que o modelo solicite explicitamente os registros excluídos sejam incluídos.Atualmente, não coletamos lixo em nenhuma tabela, exceto as que contêm blobs ou textos; o espaço é trivial se os registros estiverem bem normalizados e a indexação do
deleted
campo causa um impacto limitado na velocidade de seleção.fonte
Como alternativa, você pode colocar o ônus sobre os usuários (e desenvolvedores) e seguir uma sequência de 'Tem certeza?', 'Tem certeza?' e 'Você tem certeza absoluta, boa e verdadeira?' perguntas antes que o registro seja excluído. Ligeiramente faceta, mas vale a pena considerar.
fonte
Estou acostumado a ver linhas da tabela com colunas como 'DeletedDate' nelas e não gosto delas. A própria noção de 'excluído' é que a entrada não deveria ter sido feita em primeiro lugar. Praticamente, eles não podem ser removidos do banco de dados, mas eu não os quero com meus dados quentes. Linhas excluídas logicamente são, por definição, dados frios, a menos que alguém queira especificamente ver dados excluídos.
Além disso, toda consulta escrita deve excluí-los especificamente e os índices precisam considerá-los também.
O que eu gostaria de ver é uma alteração no nível da arquitetura do banco de dados e no nível do aplicativo: crie um esquema chamado 'excluído'. Cada tabela definida pelo usuário tem um equivalente idêntico no esquema 'excluído' com um campo extra contendo metadados - o usuário que a excluiu e quando. Chaves estrangeiras estão exigindo a criação.
Em seguida, exclusões tornam-se exclusões de inserção. Primeiro, a linha a ser excluída é inserida na sua contraparte de esquema 'excluída'. A linha em questão na tabela principal pode ser excluída. Lógica extra, no entanto, precisa ser adicionada em algum lugar ao longo da linha. Violações de chave estrangeira podem ser tratadas.
Chaves estrangeiras devem ser manuseadas adequadamente. É uma prática recomendada excluir uma linha logicamente, mas cujo principal / exclusivo tenha colunas em outras tabelas que se referem a ela. Isso não deveria acontecer de qualquer maneira. Um trabalho regular pode remover linhas de viúva (linhas cujas chaves primárias não têm referências em outras tabelas, apesar da presença de uma chave estrangeira. No entanto, essa é a lógica de negócios.
O benefício geral é a redução de metadados na tabela e a melhoria de desempenho que ela traz. A coluna 'deleteDate' diz que essa linha não deveria estar aqui, mas, por uma questão de conveniência, deixamos lá e deixamos a consulta SQL lidar com isso. Se uma cópia da linha excluída for mantida em um esquema 'excluído', a tabela principal com os dados ativos terá uma porcentagem maior de dados ativos (supondo que eles sejam arquivados em tempo hábil) e menos colunas de metadados desnecessárias. Os índices e as consultas não precisam mais considerar esse campo. Quanto menor o tamanho da linha, mais linhas podem ser ajustadas em uma página, mais rápido o SQL Server pode funcionar.
A principal desvantagem é o tamanho da operação. Agora existem duas operações em vez de uma, além da lógica extra e tratamento de erros. Isso pode levar a mais bloqueios do que a atualização de uma única coluna seria necessária. A transação mantém bloqueios na tabela por mais tempo e há duas tabelas envolvidas. Excluir dados de produção, pelo menos na minha experiência, é algo raramente feito. Mesmo assim, em uma das tabelas principais, 7,5% dos quase 100 milhões de entradas tem uma entrada na coluna 'Data excluída'.
Como resposta à pergunta, o aplicativo teria que estar ciente de 'cancelar a exclusão'. Basta fazer o mesmo na ordem inversa: insira a linha do esquema 'excluído' na tabela principal e, em seguida, exclua a linha do esquema 'excluído. Novamente, é necessária alguma lógica extra e manipulação de erros para garantir erros, problemas com chaves estrangeiras e similares.
fonte