Preciso esclarecer primeiro que a coluna status não se destina a refletir o status de um item do mundo real representado pelo registro (linha) na tabela. Em vez disso, pretende-se mostrar o status do próprio registro.
Pode ser tão simples quanto Ativo / Inativo ou complicado, como Aprovado / Excluído / Bloqueado / Pendente / Rejeitado, etc. O status pode ser armazenado em uma coluna booleana / número inteiro curto ou em uma coluna de um caractere, com mapeamentos como true
/ 1
= Ativo ou A
= Aprovado.
A idéia básica é ter um suporte de recuperação do tipo lixeira / lixo no aplicativo (e simulá-lo no banco de dados). Se houver uma GUI de front-end ou outra interface que supostamente permita que um usuário "exclua" registros, ele não exclui o registro da tabela, mas simplesmente altera o status do registro para Inativo ou Excluído. Quando a interface busca registros, ela sempre obtém os registros que correspondem apenas à condição de que o status seja Ativo ou Aprovado.
Se o usuário cometer um erro e o registro "excluído" (na perspectiva do usuário) precisar ser recuperado, um DBA poderá facilmente corrigir o registro para que fique ativo ou aprovado, o que seria melhor do que procurar backups e encontrar o registro original. há. Ou a própria interface pode permitir que o usuário visualize registros excluídos em um modo de exibição separado e restaure-os conforme necessário, ou mesmo exclua-os permanentemente (excluindo o registro real).
Minhas perguntas:
- Esta é uma boa prática ou uma má prática?
- Isso afeta a normalização dos dados?
- Quais são as possíveis armadilhas?
- Existe algum método alternativo para alcançar o mesmo objetivo? (Veja a nota)
- Como o banco de dados pode impor restrições exclusivas aos dados para apenas um determinado status (mas permitir qualquer número de duplicatas para outros status)?
- Por que os bancos de dados não fornecem um recurso semelhante a uma "lixeira" ou o rastreamento / recuperação de tabelas nativamente, para que possamos permitir que as interfaces excluam os registros reais sem se preocupar?
Nota: Li sobre a manutenção de uma tabela de histórico separada, mas isso parece pior em termos de armazenamento e a necessidade de gerar gatilhos e mantê-los atualizados com o esquema da tabela rastreada.
fonte
Respostas:
Eu sei disso como uma "exclusão suave"; apenas marcando um registro como "excluído", mesmo que não seja.
Depende.
Se isso é algo que seus usuários precisam [muito], provavelmente é uma coisa boa. Na grande maioria dos casos, porém, eu argumentaria que isso está adicionando [muita] sobrecarga por pouco benefício.
Não, mas vai afetar a sua indexação desses dados.
Certifique-se de incluir a coluna "excluída" em seus índices, para que essas linhas sejam excluídas o mais cedo possível em suas consultas.
Seus dados se tornam um pouco mais complexos. Tudo o que chega perto dos dados precisa "saber" sobre esses registros extras "não realmente existem". Ou então, você deve criar modos de exibição nessas tabelas que excluem essas linhas e usá-los em, digamos, na sua Ferramenta de escolha de relatórios.
Seu banco de dados pode aumentar de tamanho. Se você realmente não está excluindo essas linhas, elas ainda estão lá, ocupando espaço. Isso pode ou não ser um problema, especialmente porque você os incluiu em seus índices, portanto o espaço que eles consomem é multiplicado.
Não, realmente não.
Não facilmente. A Integridade Referencial Declarativa (cláusulas de chave estrangeira) é a maneira mais limpa de implementar isso e é fácil para coisas como ferramentas de Relatório seguir essas regras para determinar os relacionamentos entre as tabelas. Tais regras se aplicam a todos os registros, independentemente do "status" (e não há como contornar isso).
A alternativa é usar Triggers, trechos de código processual que reforçam a integridade referencial entre tabelas e executam todas as coisas inteligentes e condicionais necessárias. Isso é bom para o seu caso em particular, mas a maioria dos benefícios do RI declarativo sai da janela - não há relacionamentos [externamente] detectáveis entre suas tabelas; isso é tudo "oculto" nos gatilhos.
Por que eles?
Afinal, esses são bancos de dados, não sistemas de arquivos ou planilhas.
O que eles fazem, eles [podem] fazer muito, muito bem.
O que eles não fazem, provavelmente não houve muita demanda.
fonte
É uma prática. Se é bom ou ruim, depende muito do seu aplicativo e da frequência com que você realmente precisará / deseja fazer uma "exclusão". Eu duvido muito de um plano para colocar esse tipo de coluna de todas as tabelas no sistema - parece altamente improvável que você realmente se incomode em implementar a exclusão de exclusão em todas as tabelas do sistema. E isso requer implementação - na grande maioria dos casos, você não está excluindo uma única linha de uma única tabela, é necessário percorrer as tabelas filho, excluindo as linhas e atualizando as tabelas relacionadas.
Para a maioria das perguntas, é altamente dependente da implementação. Por exemplo, o Oracle fornece métodos diferentes para rastrear todas as alterações em uma tabela - o Flashback Data Archive (FDA também conhecido como Total Recall) é a abordagem mais recente para manter um histórico completo de todas as versões de uma linha e arquivamento no banco de dados para implementar o padrão de exclusão suave. Outros bancos de dados podem fornecer outras maneiras de implementar o padrão. Dependendo do banco de dados e de como você implementa a exclusão reversa, haverá vários impactos no desempenho, se e como as restrições podem ser impostas etc. Se estamos falando do Oracle, você pode fazer muito com índices baseados em funções, por exemplo , no SQL Server, você pode usar índices filtrados para fins semelhantes.
fonte
É muito comum usar um campo "sinalizado para exclusão" nos sistemas MRP / ERP.
Por exemplo, pode-se marcar um registro de peça ou estoque que não é mais vendido como inativo, mas ainda existem pedidos pendentes associados a ele. Fazer uma exclusão real no registro pode afetar os pedidos que ainda não foram enviados, as entradas do razão que ainda não foram lançadas, as tabelas de histórico que não serão construídas até o final do mês etc. Muitos sistemas proibirão uma exclusão de registro, a menos que ela passe uma série de validações em relação a outras tabelas. Se você estiver excluindo em cascata os relacionamentos, uma exclusão real poderá ser ainda mais destrutiva.
Em vez disso, sinalizando-o para exclusão, você coloca um marcador de intenção claro no registro e, mais tarde, uma tarefa agendada pode excluir o registro se verificar que todas as tabelas relacionadas não estão mais fazendo referência a ele.
Um caso semelhante poderia ser feito para esse recurso em uma tabela de clientes e outras tabelas "de longo prazo". Até faz sentido em tabelas mais voláteis, como pedidos, embora o nome da bandeira possa se tornar algo como "enviado" ou "cancelado". Ele tem a mesma função: não o exclua neste segundo, mas use-o como um sinalizador para o programa de limpeza, para tentar validar a exclusão do registro no futuro.
fonte
Como solução alternativa, o uso da fonte de eventos permite objetivos semelhantes sem complicar a estrutura da tabela, embora isso torne o código para modificar seus dados um pouco mais complexo, pois é necessário gravar a modificação em um evento que possa persistir em um histórico de eventos. . Isso permite recriar o banco de dados como era a qualquer momento, o que pode ser um recurso muito útil.
(Eu não acredito que isso seja o que você quis dizer com "tabela de histórico", que eu acho que você quis dizer simplesmente copiar registros modificados ou excluídos em outra tabela antes de alterá-los)
fonte
Eu vejo e uso esse padrão frequentemente para esses casos de uso:
O problema é impor a integridade dos dados se mais de um aplicativo ou serviço da Web estiver gravando em tabelas. Como você garante que, para um caso, exista apenas um estado atual? Como Justin Cave ressalta, isso pode ser feito no Oracle criando um índice virtual baseado em uma função, mas essa sobrecarga extra para o que originalmente parecia um conceito simples.
fonte
É uma boa prática se você planeja usar seus dados para gerar relatórios (qualquer aplicativo grande o suficiente precisaria ter relatórios).
Para acelerar seu aplicativo, você realmente não deve permitir que ferramentas de relatório sejam executadas em seu banco de dados. Como tal, você precisará fazer uma cópia / sincronização para outro banco de dados.
Uso
recordStatus
apenas dois estadosACTIVE
ouCANCELLED
em combinação com umlastUpdatedOn
carimbo de data / hora. Eu uso emrecordStatus
vez dostatus
que geralmente tem um significado comercial.Quando estou sincronizando o banco de dados de relatórios com o aplicativo, faço um filtro
lastUpdatedOn
para saber quais substituirei no lado dos relatórios.No lado do relatório, não terei os campos
recordStatus
oulastUpdatedOn
, pois geralmente não serão relatados. Como tal, quando vejo umCANCELLED
status, excluo o registro do lado do relatório para que ele tenha apenas registros ativos.Isso pode ser expandido para outros tipos de lojas, como arquivos ou backups, nos quais a sincronização quase completa é necessária. No entanto, o relatório é o objetivo mais comum.
Observe o seu exemplo de
Approved
,New
,Pending
não é uma boa idéia de colocar como um campo comum, como que tem um negócio que significa que ele deve ir só para onde faz negócios sentido sábio.Quanto ao bloqueio, use o
versionNo
que fornece um bloqueio otimista para o seu registro.Outra opção, em vez de,
recordStatus
érecordActive
armazená-la como umaboolean
que ocupa menos espaço e menos indexação, mas eu ficaria preocupado com as necessidades futuras que você talvez não preveja.fonte