Comment contrôler la version d'un enregistrement dans une base de données

disons que j'ai un enregistrement dans la base de données et que l'administrateur et les utilisateurs normaux peuvent faire des mises à jour.

est-ce que n'importe qui peut suggérer une bonne approche/architecture comment contrôler chaque changement de version dans cette table de sorte qu'il est possible de faire reculer un enregistrement à une révision précédente.

135
demandé sur Benjamin 2008-11-27 09:39:28

11 réponses

disons que vous avez une table FOO que les administrateurs et les utilisateurs peuvent mettre à jour. La plupart du temps, vous pouvez écrire des requêtes contre la table de FOO. Les jours heureux.

alors, je créerais une table FOO_HISTORY . Il contient toutes les colonnes du tableau FOO . La clé primaire est la même que FOO plus une colonne RevisionNumber. Il y a une clé étrangère de FOO_HISTORY à FOO . Vous pouvez également ajouter des colonnes relatives à la révision comme L'identifiant D'Utilisateur et RevisionDate. Peupler les nombres de révision d'une manière toujours croissante à travers toutes les tables *_HISTORY (c.-à-d. à partir d'une séquence Oracle ou équivalent). Ne comptez pas sur le fait qu'il n'y a qu'un changement par seconde (c.-à-D. Ne mettez pas RevisionDate dans la clé primaire).

maintenant, chaque fois que vous mettez à jour FOO , juste avant de faire la mise à jour, vous insérez les anciennes valeurs dans FOO_HISTORY . Vous faites cela à un certain niveau fondamental dans votre conception de sorte que les programmeurs ne peuvent pas accidentellement sauter cette étape.

Si vous souhaitez supprimer une ligne de FOO , vous avez quelques choix. Soit cascade et supprimer toute l'histoire, ou effectuer une suppression logique en signalant FOO comme supprimé.

cette solution est bonne quand vous êtes largement intéressé par les valeurs actuelles et seulement de temps en temps dans l'histoire. Si vous avez toujours besoin de l'historique, vous pouvez inscrire les dates de début et de fin effectives et conserver tous les documents dans FOO lui-même. Chaque requête doit alors vérifier ces dates.

137
répondu WW. 2018-05-21 19:48:34

je pense que vous êtes à la recherche de versioning le contenu des enregistrements de base de données (comme le fait StackOverflow quand quelqu'un édite une question/réponse). Un bon point de départ pourrait être la recherche d'un modèle de base de données qui utilise le suivi révision .

le meilleur exemple qui vient à l'esprit est MediaWiki, le moteur de Wikipedia. Comparer le diagramme de la base de données ici , en particulier le table de révision .

selon les technologies que vous utilisez, vous devrez trouver de bons algorithmes de diff/Fusion.

Vérifier cette question si c'est pour .NET.

37
répondu CMS 2017-05-23 11:47:13

dans le monde BI, vous pouvez accomplir ceci en ajoutant un startDate et endDate à la table que vous voulez version. Lorsque vous insérez le premier enregistrement dans la table, la date start est peuplée, mais la date end est nulle. Lorsque vous insérez le second enregistrement, vous mettez aussi à jour la date de fin du premier enregistrement avec la date de début du second enregistrement.

lorsque vous voulez voir l'enregistrement en cours, vous sélectionnez celui où endDate est null.

C'est parfois appelé un type 2 lentement changer la Dimension . Voir aussi TupleVersioning "151960920

22
répondu Dave Neeley 2008-11-27 07:09:35

mise à niveau en SQL 2008.

essayez D'utiliser SQL Change Tracking, en SQL 2008. Au lieu de l'horodatage et des hacks de colonnes de pierre tombale, vous pouvez utiliser cette nouvelle fonctionnalité pour suivre les changements sur les données dans votre base de données.

MSDN SQL 2008 Change Tracking

7
répondu D3vtr0n 2008-11-28 00:36:38

voulait juste ajouter qu'une bonne solution à ce problème est d'utiliser une base de données temporelle . De nombreux fournisseurs de bases de données offrent cette fonctionnalité soit hors de la boîte ou via une extension. J'ai utilisé avec succès la table temporelle extension avec PostgreSQL mais d'autres l'ont aussi. Chaque fois que vous mettez à jour un enregistrement dans la base de données, la base de données tient sur la version précédente de cet enregistrement aussi.

5
répondu wuher 2015-12-10 03:29:28

deux options:

  1. ont une table d'histoire - insérez les anciennes données dans cette table d'Histoire chaque fois que l'original est mis à jour.
  2. tableau de vérification-stocker les valeurs avant et après - juste pour les colonnes modifiées dans un tableau de vérification avec d'autres informations comme qui a mis à jour et quand.
4
répondu alok 2008-11-27 09:04:42

vous ne dites pas quelle base de données, et je ne le vois pas dans les étiquettes postales. Si C'est pour Oracle, je peux vous recommander l'approche qui est intégrée dans Designer: utilisez tables de journal . Si c'est pour une autre base de données, je recommande la même chose...

la façon dont il fonctionne, au cas où vous voulez le répliquer dans un autre DB, ou peut-être si vous voulez juste le comprendre, est que pour une table il y a une table d'ombre créée aussi, juste une normale table de la base de données, avec les mêmes spécifications de champ, plus quelques champs supplémentaires: comme quelle action a été prise en dernier (chaîne, valeurs typiques "INS" pour insert, "UPD" pour update et "DEL" pour delete), datetime pour quand l'action a eu lieu, et ID de l'utilisateur pour qui l'a fait.

par des déclencheurs, chaque action à n'importe quelle ligne dans le tableau insère une nouvelle ligne dans la table de journal avec les nouvelles valeurs, quelle action a été prise, quand, et par quel utilisateur. Vous ne supprimez jamais aucune ligne (au moins pas pour les quelques derniers mois). Oui, il va grossir, facilement des millions de lignes, mais vous pouvez facilement suivre la valeur pour n'importe quel record à n'importe quel point dans le temps depuis le début de la journalisation ou les vieilles lignes de journal a été purgé en dernier, et qui a fait le dernier changement.

dans Oracle tout ce dont vous avez besoin est généré automatiquement en code SQL, Tout ce que vous avez à faire est de compiler/exécuter; et il est livré avec une application de base CRUD (en fait seul "R") pour l'inspecter.

3
répondu bart 2008-11-27 08:10:42

vous pouvez effectuer l'audit sur une table SQL via des déclencheurs SQL. À partir d'un déclencheur, vous pouvez accéder à 2 tables spéciales ( insérée et supprimée ). Ces tableaux contiennent les lignes exactes qui ont été insérées ou supprimées chaque fois que le tableau est mis à jour. Dans le trigger SQL vous pouvez prendre ces lignes modifiées et les insérer dans la table d'audit. Cette approche signifie que votre audit est transparent pour le programmeur; ne nécessitant aucun effort de leur part ou de toute connaissance de mise en œuvre.

le bonus supplémentaire de cette approche est que l'audit aura lieu indépendamment du fait que l'opération sql ait eu lieu via vos DLLs d'accès aux données, ou via une requête SQL manuelle; (comme l'audit est effectué sur le serveur lui-même).

2
répondu Doctor Jones 2010-11-05 13:33:45

je fais aussi la même chose. Je crée une base de données pour les plans de leçon. Ces plans ont besoin d'une flexibilité de conversion atomique. En d'autres termes, chaque changement, peu importe comment petit, les plans de leçon doit être autorisée, mais l'ancienne version doit être conservé intact. De cette façon, les concepteurs de leçons peuvent modifier les plans de leçon pendant que les élèves les utilisent.

La façon dont il devrait fonctionner qu'une fois qu'un élève a fait une leçon, leurs résultats sont attachés à la version ils ont terminé. Si un changement est fait, leurs résultats indiqueront toujours leur version.

de cette façon, si un critère de leçon est supprimé ou déplacé, leurs résultats ne changeront pas.

la façon dont je fais actuellement ceci est en traitant toutes les données dans un tableau. Normalement, j'aurais juste un champ id, mais avec ce système, j'utilise un id et un sub_id. Le sub_id reste toujours dans la rangée, à travers les mises à jour et les suppressions. L'id est incrémenté automatiquement. Le le logiciel du plan de leçon sera relié au plus récent sous-ID. Les résultats de l'élève seront liés à l'id. J'ai aussi inclus un horodatage pour suivre les changements, mais il n'est pas nécessaire de gérer le versioning.

une chose que je pourrais changer, une fois que je l'ai testé, est que je pourrais utiliser l'idée d'endDate nulle mentionnée précédemment. Dans mon système, pour trouver la version la plus récente, je dois trouver le max(id). L'autre système recherche simplement endDate = null. Pas sûr si les avantages outway ayant un autre champ date.

mes deux cents.

2
répondu Jordan 2012-10-22 23:55:32

While @WW. réponse est une bonne réponse une autre façon est de faire une colonne de version et de garder toutes vos versions dans la même table.

pour une approche de table vous soit:

  • utilisez un drapeau pour indiquer le dernier ala Word Press
  • "
  • OU de faire un méchant plus que la version outer join .

An L'exemple SQL de la méthode outer join utilisant les numéros de révision est:

SELECT tc.*
FROM text_content tc
LEFT OUTER JOIN text_content mc ON tc.path = mc.path
AND mc.revision > tc.revision
WHERE mc.revision is NULL 
AND tc.path = '/stuff' -- path in this case is our natural id.

la mauvaise nouvelle est que ce qui précède nécessite un outer join et les jointures externes peuvent être lentes. La bonne nouvelle est que la création de nouvelles entrées est théoriquement moins cher parce que vous pouvez le faire dans une opération d'écriture avec les transactions (en supposant que votre base de données est atomique).

un exemple faisant une nouvelle révision pour '/stuff' pourrait être:

INSERT INTO text_content (id, path, data, revision, revision_comment, enabled, create_time, update_time)
(
SELECT
(md5(random()::text)) -- {id}
, tc.path
, 'NEW' -- {data}
, (tc.revision + 1)
, 'UPDATE' -- {comment}
, 't' -- {enabled}
, tc.create_time
, now() 
FROM text_content tc
LEFT OUTER JOIN text_content mc ON tc.path = mc.path
AND mc.revision > tc.revision
WHERE mc.revision is NULL 
AND tc.path = '/stuff' -- {path}
)

nous insérons en utilisant les anciennes données. Ceci est particulièrement utile si vous dites que vous vouliez seulement mettre à jour une colonne et éviter des fermetures et / ou des transactions optimistes.

la table approche par drapeau et approche historique exige que deux" lignes 1519110920 " soient insérées/mises à jour.

l'autre avantage de l'approche du numéro de révision outer join est que vous pouvez toujours effectuer une révision de l'approche des tables multiples plus tard avec déclencheurs parce que votre déclencheur devrait essentiellement faire quelque chose comme ce qui précède.

2
répondu Adam Gent 2012-11-28 02:37:24

Alok suggéré Audit table ci-dessus, je voudrais l'expliquer dans mon post.

j'ai adopté cette conception de table simple sans schéma sur mon projet.

schéma:

  • id-INTEGER AUTO INCREMENT
  • nom d'utilisateur - CHAÎNE
  • tablename-STRING
  • oldvalue-TEXT /JSON
  • nouvelle valeur-texte / JSON
  • createdon-DATETIME

ce tableau peut contenir des enregistrements historiques pour chaque tableau en un seul endroit, avec l'historique complet de l'objet dans un enregistrement. Cette table peut être remplie en utilisant des triggers / hooks où les données changent, en stockant des snapshots de valeurs anciennes et nouvelles de la ligne cible.

Pros avec cette conception:

  • moins de tables à gérer pour l'histoire gestion.
  • stocke un instantané complet de chaque rangée ancienne et nouvelle état.
  • facile à rechercher sur chaque table.
  • peut créer une partition par table.
  • peut définir la Politique de conservation des données par table.

Cons avec cette conception:

  • la taille des données peut être grande, si le système a des changements fréquents.
1
répondu Hassan Farid 2018-08-03 18:36:00