web-dev-qa-db-fra.com

Est-ce une mauvaise pratique d'avoir une colonne "statut d'enregistrement" dans une table de base de données?

Je dois d'abord préciser que la colonne d'état est pas destinée à refléter l'état d'un élément du monde réel représenté par le enregistrement (ligne) dans le tableau. Il vise plutôt à montrer l'état de l'enregistrement lui-même.

Il peut être aussi simple qu'Actif/Inactif ou compliqué comme Approuvé/Supprimé/Verrouillé/En attente/Rejeté, etc. Le statut peut être stocké sur une colonne booléenne/entière courte ou une colonne à un seul caractère, avec des mappages comme true/1 = Actif ou A = Approuvé.

L'idée de base est d'avoir un support de récupération de type corbeille/corbeille dans l'application (et de le simuler dans la base de données). S'il existe une interface graphique frontale ou une autre interface qui peut soi-disant permettre à un utilisateur de "supprimer" des enregistrements, il ne supprime pas réellement l'enregistrement dans le tableau, mais modifie simplement l'état de l'enregistrement en Inactif ou Supprimé. Lorsque l'interface récupère des enregistrements, elle obtient toujours les enregistrements qui correspondent uniquement à la condition que l'état est Actif ou Approuvé.

Si l'utilisateur fait une erreur et que l'enregistrement "supprimé" (du point de vue de l'utilisateur) doit être récupéré, un administrateur de base de données peut facilement corriger l'enregistrement pour qu'il soit actif ou approuvé, ce qui serait mieux que de rechercher des sauvegardes et, espérons-le, de trouver l'enregistrement d'origine Là. Ou l'interface elle-même peut permettre à l'utilisateur de visualiser les enregistrements supprimés dans une vue distincte, et de les restaurer selon les besoins, ou même de les supprimer définitivement (suppression de l'enregistrement réel).

Mes questions:

  • Est-ce une bonne ou une mauvaise pratique?
  • Cela affecte-t-il la normalisation des données?
  • Quels sont les pièges potentiels?
  • Existe-t-il une autre méthode pour atteindre le même objectif? (voir la note)
  • Comment pouvez-vous faire en sorte que la base de données applique des contraintes uniques sur les données pour un certain statut uniquement (mais autorise un nombre illimité de doublons pour d'autres statuts)?
  • Pourquoi les bases de données ne fournissent-elles pas une fonctionnalité de type "corbeille" ou un suivi/récupération de table en mode natif, afin que nous puissions laisser les interfaces supprimer les enregistrements réels sans souci?

Remarque: J'ai lu comment gérer une table d'historique distincte, mais cela semble pire en termes de stockage et d'avoir à générer des déclencheurs et à maintenir les déclencheurs à jour avec le schéma de la table suivie.

12
ADTC

Je le connais comme un "Soft Delete"; il suffit de marquer un enregistrement comme "supprimé", même s'il ne l'est vraiment pas.

Est-ce une bonne ou une mauvaise pratique?

Ça dépend.
Si c'est quelque chose dont vos utilisateurs ont besoin [beaucoup], c'est probablement une bonne chose. Dans la grande majorité des cas, cependant, je dirais que cela ajoute [beaucoup] de frais généraux pour peu d'avantages.

Cela affecte-t-il la normalisation des données?

Non, mais cela va affecter votre indexation de ces données.
Veillez à inclure la colonne "supprimé" dans vos index, afin que ces lignes soient exclues le plus tôt possible dans vos requêtes.

Quels sont les pièges potentiels?

Vos données deviennent un peu plus complexes. Tout ce qui se rapproche des données doit "connaître" ces enregistrements supplémentaires, "pas vraiment là". Ou, vous devez créer des vues sur ces tables qui excluent ces lignes et utiliser ces vues dans, disons, votre outil de création de rapports préféré.

Votre base de données peut augmenter en taille. Si vous ne supprimez pas vraiment ces lignes, elles sont toujours là, occupant de l'espace. Cela peut ou non être un problème, d'autant plus que vous les avez inclus dans vos index, donc l'espace qu'ils consomment est multiplié.

Existe-t-il une autre méthode pour atteindre le même objectif? (voir la note)

Non, pas vraiment.

Comment pouvez-vous faire en sorte que la base de données applique des contraintes uniques sur les données pour un certain statut uniquement (mais autorise un nombre illimité de doublons pour d'autres statuts)?

Pas facilement. L'intégrité référentielle déclarative (clauses de clé étrangère) est le moyen le plus propre de l'implémenter et il est facile pour des choses comme les outils de reporting de reprendre ces règles pour déterminer les relations entre les tables. Ces règles s'appliquent à tous les enregistrements, quel que soit leur "statut" (et il n'y a aucun moyen de contourner cela).

L'alternative consiste à utiliser des déclencheurs, des extraits de code procédural qui renforcent l'intégrité référentielle entre les tables et effectuent toutes les tâches intelligentes et conditionnelles dont vous avez besoin. C'est bon pour votre cas particulier, mais la plupart des avantages du Déclaratif R.I. passent par la fenêtre - il n'y a pas de relations [externes] détectables entre vos tables; c'est tout "caché" dans les déclencheurs.

Pourquoi les bases de données ne fournissent-elles pas une fonctionnalité de type "corbeille" ou un suivi/récupération de table en mode natif, afin que nous puissions laisser les interfaces supprimer les enregistrements réels sans souci?

Pourquoi serait ils?

Ce sont des bases de données, après tout, pas des systèmes de fichiers ou des feuilles de calcul.

Ce qu'ils font, ils [peuvent] le faire très, très bien.

Ce qu'ils ne font pas, il n'y a probablement pas eu beaucoup de demande.

5
Phill W.

C'est une pratique. Que ce soit bon ou mauvais dépend fortement de votre application et de la fréquence à laquelle vous allez vraiment avoir besoin/vouloir faire une "suppression". Je serais assez douteux d'un plan pour mettre ce type de colonne de chaque table dans le système - il semble très improbable que vous vous donniez vraiment la peine d'implémenter undelete sur chaque table du système. Et cela nécessite une implémentation: dans la grande majorité des cas, vous ne supprimez pas une seule ligne d'une seule table, vous devez parcourir les tables enfants en supprimant les lignes et en mettant à jour les tables associées.

Pour la plupart des autres questions, cela dépend fortement de l'implémentation. Par exemple, Oracle fournit différentes méthodes pour suivre toutes les modifications apportées à une table - Flashback Data Archive (FDA également connu sous le nom de Total Recall) étant l'approche la plus récente pour maintenir un historique complet de chaque version d'une ligne et l'archivage dans la base de données pour la mise en œuvre le motif de suppression progressive. D'autres bases de données peuvent fournir d'autres moyens d'implémenter le modèle. Selon la base de données et la façon dont vous implémentez la suppression logicielle, il y aura divers impacts sur les performances, si et comment les contraintes peuvent être appliquées, etc. Si nous parlons d'Oracle, vous pouvez faire beaucoup avec les index basés sur les fonctions, par exemple , dans SQL Server, vous pouvez souvent utiliser des index filtrés à des fins similaires.

9
Justin Cave

Il est très courant d'utiliser un champ "marqué pour suppression" dans les systèmes MRP/ERP.

Par exemple, on peut vouloir marquer un enregistrement de pièce ou d'inventaire qui n'est plus vendu comme inactif, mais il y a encore des commandes en cours qui lui sont associées. Effectuer une véritable suppression de l'enregistrement peut affecter les commandes qui n'ont pas encore été expédiées, les entrées de grand livre qui n'ont pas encore été enregistrées, les tables d'historique qui ne seront pas construites avant la fin du mois, etc. De nombreux systèmes interdiront la suppression d'un enregistrement à moins qu'il ne passe une série de validations par rapport à d'autres tables. Si vous supprimez en cascade vos relations, une véritable suppression peut être encore plus destructrice.

Au lieu de cela, en le marquant pour suppression, vous placez un marqueur d'intention clair sur l'enregistrement et, plus tard, une tâche planifiée peut supprimer l'enregistrement s'il vérifie que toutes les tables associées ne le référencent plus.

Un cas similaire pourrait être fait pour cette fonctionnalité sur une table client et d'autres tables "à long terme". Cela a même du sens sur des tables plus volatiles comme les commandes, bien que le nom du drapeau puisse devenir quelque chose comme "expédié" ou "annulé". Il remplit la même fonction: ne le supprimez pas cette seconde, mais utilisez-le comme indicateur pour le programme de purge afin qu'il tente de valider la suppression de l'enregistrement à l'avenir.

4

Comme solution alternative, l'utilisation de sourcing d'événements permet des objectifs similaires sans compliquer la structure de la table, même si cela rend le code de modification de vos données un peu plus complexe, car vous devez écrire la modification dans un événement qui peut être conservé dans l'historique des événements. Cela vous permet ensuite de recréer la base de données telle qu'elle était à un moment donné, ce qui peut être une fonctionnalité très utile.

(Je ne crois pas que ce soit ce que vous vouliez dire par "table d'historique", ce que je pense que vous vouliez simplement copier les enregistrements modifiés ou supprimés dans une autre table avant de les changer)

3
Jules

C'est une bonne pratique si vous prévoyez d'utiliser vos données pour la génération de rapports (toute application suffisamment grande devrait avoir des rapports).

Afin d'accélérer votre application, vous ne devez vraiment pas laisser les outils de reporting s'exécuter sur votre base de données. En tant que tel, vous devez effectuer une copie/synchronisation vers une autre base de données.

J'utilise recordStatus de seulement deux états ACTIVE ou CANCELLED en combinaison avec un horodatage lastUpdatedOn. J'utilise recordStatus plutôt que status qui a généralement une signification commerciale.

Lorsque je synchronise la base de données de rapports avec l'application, je fais un filtre sur lastUpdatedOn pour savoir lesquels je vais remplacer du côté des rapports.

Du côté des rapports, je n'aurai pas les champs recordStatus ou lastUpdatedOn car ils ne seront généralement pas signalés. En tant que tel, lorsque je vois un état CANCELLED, je supprimerais l'enregistrement du côté du rapport de cette façon, il n'a que des enregistrements actifs.

Cela peut être étendu à d'autres types de magasins tels que les archives ou les sauvegardes où une synchronisation presque complète est requise. Cependant, la notification est l'objectif le plus courant.

Notez que votre exemple de Approved, New, Pending n'est PAS une bonne idée de mettre en tant que champ commun car cela a une signification commerciale, il ne doit aller que là où cela a du sens affaires.

Quant au verrou, utilisez versionNo qui fournit un verrou optimiste pour votre enregistrement.

Une autre option au lieu de recordStatus est recordActive et le stocke en tant que boolean qui prend moins d'espace et moins d'indexation, mais je serais préoccupé par les besoins futurs que vous pourriez ne pas prévoir.

1

Je vois et utilise fréquemment ce modèle pour ces cas d'utilisation:

  • des métadonnées où vous souhaitez uniquement afficher les valeurs en vigueur aujourd'hui. Par exemple, pour choisir dans une liste de constructeurs automobiles dans une liste déroulante où enabled = 1 les valeurs des tableaux pour ID, VALUE, ENABLED sont 1, 'Ford', 1 et 2, 'Edsel', 0, 3, 'Toyota' , 1 ne donne que les choix de Ford et Toyota
  • pour un système de gestion de cas où le paradigme est qu'un cas ne peut être que dans un état à la fois. Dans ce cas, la colonne à bascule était appelée CURRENT avec des valeurs de 0 ou 1 appliquées par des contraintes de vérification. Lorsqu'un cas passe d'un état à un autre, l'application met à jour l'indicateur CURRENT de l'ancien état à 0 et le nouveau à 1.

Le problème consiste à appliquer l'intégrité des données si plusieurs applications ou services Web écrivent dans des tables. Comment vous assurez-vous que pour un cas, il n'y a qu'un seul état actuel? Comme Justin Cave le fait remarquer, cela peut être fait dans Oracle en créant un index virtuel basé sur une fonction mais cette surcharge supplémentaire pour ce qui semblait à l'origine un concept simple.

1
kevinsky