É uma prática ruim ter uma coluna "status do registro" em uma tabela de banco de dados?

12

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.

ADTC
fonte
O problema com as restrições exclusivas (que você já nomeou) é exatamente o motivo pelo qual as tabelas de histórico geralmente são preferíveis - você pode manter as restrições de chave exclusivas nas tabelas originais e não adicioná-las à tabela de histórico. usar opções de armazenamento específicas (dependentes do banco de dados) para elas, para que elas geralmente sejam melhores em termos de armazenamento, e não piores. Quando você possui muitas dessas tabelas, os gatilhos e as tabelas de histórico não devem ser manuscritos, mas gerados, o que resolverá o problema de como mantê-los "atualizados".
Doc Brown

Respostas:

5

Eu sei disso como uma "exclusão suave"; apenas marcando um registro como "excluído", mesmo que não seja.

Esta é uma boa prática ou uma má prática?

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.

Isso afeta a normalização dos dados?

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.

Quais são as possíveis armadilhas?

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.

Existe algum método alternativo para alcançar o mesmo objetivo? (Veja a nota)

Não, realmente não.

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)?

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 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?

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.

Phill W.
fonte
Boa resposta, mas existem opções alternativas, por exemplo, mova as linhas para uma tabela de backup de onde você pode recuperá-las. A tabela de backup pode ter índices mínimos. Isso minimiza os problemas que você observa com a abordagem existente (índice maior, confusão potencial para os usuários da tabela, etc.), mas obviamente adiciona o fato de que você tem outra tabela para manter (e significa que as entradas foram escritas para referências a chaves estrangeiras). Existem algumas outras opções - mas, na verdade, as que vêm à mente são todas alguma implementação personalizada, não algo geral fornecido por todos os bancos de dados SQL para esses casos.
22819 Frank Hopkins
9

É 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.

Justin Cave
fonte
O Oracle Flashback é exatamente a solução ideal para o que eu quero. Pena que é proprietário da Oracle.
ADTC
4

É 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.

Mike apoia Monica
fonte
3

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)

Jules
fonte
Conceito interessante. Vou analisar como isso pode ser implementado.
ADTC 10/03/16
1

Eu vejo e uso esse padrão frequentemente para esses casos de uso:

  • metadados em que você deseja exibir apenas os valores que estão em vigor hoje. Por exemplo, para escolher de uma lista de fabricantes de carros em uma lista suspensa onde ativado = 1, os valores das tabelas para ID, VALUE, ATIVADO são 1, 'Ford', 1 e 2, 'Edsel', 0, 3, 'Toyota' , 1 dá apenas as opções de Ford e Toyota
  • para um sistema de gerenciamento de casos em que o paradigma é que um caso pode estar apenas em um estado por vez. Nesse caso, a coluna de alternância foi chamada CURRENT com valores de 0 ou 1 aplicados por restrições de verificação. Como um caso se move de um estado para outro, o aplicativo atualiza o sinalizador ATUAL do estado antigo para 0 e o novo para 1

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.

kevinsky
fonte
1

É 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 recordStatusapenas dois estados ACTIVEou CANCELLEDem combinação com um lastUpdatedOncarimbo de data / hora. Eu uso em recordStatusvez do statusque geralmente tem um significado comercial.

Quando estou sincronizando o banco de dados de relatórios com o aplicativo, faço um filtro lastUpdatedOnpara saber quais substituirei no lado dos relatórios.

No lado do relatório, não terei os campos recordStatusou lastUpdatedOn, pois geralmente não serão relatados. Como tal, quando vejo um CANCELLEDstatus, 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, Pendingnã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 versionNoque fornece um bloqueio otimista para o seu registro.

Outra opção, em vez de, recordStatusé recordActivearmazená-la como uma booleanque ocupa menos espaço e menos indexação, mas eu ficaria preocupado com as necessidades futuras que você talvez não preveja.

Archimedes Trajano
fonte