Question

(Pas lié à la gestion du schéma de la base de données)

Les applications qui interagissent avec des bases de données ont souvent des objets de domaine composés de données provenant de nombreuses tables. Supposons que l’application prenne en charge le contrôle de version, au sens de CVS, pour ces objets de domaine.

Pour certains objets de domaine arbitry, comment concevriez-vous un schéma de base de données pour gérer cette exigence? Une expérience à partager?

Était-ce utile?

La solution

Réfléchissez bien aux conditions requises pour les révisions. Une fois que votre base de code aura intégré le suivi de l'historique dans le système opérationnel, il deviendra très complexe. Assurance souscription systèmes sont particulièrement dommageables pour cela, avec des schémas dépassant souvent 1000 tables. Les requêtes ont également tendance à être assez complexes, ce qui peut entraîner des problèmes de performances.

Si l'état historique n'est vraiment requis que pour la création de rapports, envisagez de mettre en place un système transactionnel "d'état actuel" avec une structure d'entrepôt de données suspendue pour l'historique de suivi. Les Dimensions en évolution lente sont une structure beaucoup plus simple pour suivre l'état de l'historique que d'essayer d'incorporer un historique ad hoc. mécanisme de suivi directement dans votre système opérationnel.

De même, la la capture de données modifiée est plus simple pour un système "en cours" avec des modifications en cours aux enregistrements en place - les clés primaires des enregistrements ne changent pas, vous n'avez donc pas besoin de faire correspondre les enregistrements contenant différentes versions de la même entité. Un mécanisme CDC efficace rendra un processus de charge d’entrepôt incrémental relativement léger et possible de s’exécuter assez fréquemment. Si vous n'avez pas besoin d'un suivi à la minute près de l'état historique (presque, mais pas tout à fait, et d'oxymoron), cela peut être une solution efficace avec une base de code beaucoup plus simple qu'un mécanisme de suivi d'histoire complet intégré directement dans l'application.

Autres conseils

Une technique que j’avais utilisée pour cela dans ce passé consistait à adopter un concept de "générations". Dans la base de données, chaque modification incrémente le numéro de génération actuel de la base de données. Si vous utilisez Subversion, pensez aux révisions. Chaque enregistrement est associé à 2 numéros de génération (2 colonnes supplémentaires sur les tables) - la génération pour laquelle l'enregistrement commence à être valide et la génération pour laquelle il cesse d'être valide. Si les données sont actuellement valides, le deuxième nombre serait NULL ou un autre marqueur générique.

Donc, pour insérer dans la base de données:

  1. incrémente le numéro de génération
  2. insérez les données
  3. marque la durée de vie de ces données avec une valeur valide de, et une valeur valide de NULL

Si vous mettez à jour des données:

  1. marque toutes les données sur le point d'être modifiées comme valides par rapport au numéro de génération actuel
  2. incrémente le numéro de génération
  3. insérez les nouvelles données avec le numéro de génération actuel

La suppression consiste simplement à marquer les données comme se terminant à la génération actuelle.

Pour obtenir une version particulière des données, recherchez la génération souhaitée et recherchez des données valables entre ces versions.

Exemple:

Créer une personne.

|Name|D.O.B  |Telephone|From|To  |
|Fred|1 april|555-29384|1   |NULL|

Mettre à jour le numéro de téléphone

|Name|D.O.B  |Telephone|From|To  |
|Fred|1 april|555-29384|1   |1   |
|Fred|1 april|555-43534|2   |NULL|

Supprimer fred:

|Name|D.O.B  |Telephone|From|To  |
|Fred|1 april|555-29384|1   |1   |
|Fred|1 april|555-43534|2   |2   |

Une alternative au contrôle de version strict consiste à scinder les données en 2 tables: actuelle et historique.

La table actuelle contient toutes les données en temps réel et bénéficie de toutes les performances que vous intégrez. Toutes les modifications écrivent d’abord les données actuelles dans le "historique" associé. tableau avec un marqueur de date qui indique quand il a changé.

Si vous utilisez Hibernate, JBoss Envers pourrait être une option. Il vous suffit d'annoter les classes avec @Audited pour conserver leur historique.

Vous aurez besoin d’une fiche maîtresse dans une table maîtresse contenant les informations communes à toutes les versions.

Ensuite, chaque table enfant utilise l'ID d'enregistrement principal + le numéro de version dans la clé primaire.

Cela peut être fait sans la table maître, mais d'après mon expérience, les instructions SQL auront tendance à rendre les instructions SQL plus compliquées.

Une méthode simple et infaillible consiste à ajouter une colonne de version à vos tables, à stocker la version de l'objet et à choisir la logique d'application appropriée en fonction de ce numéro de version. De cette façon, vous obtenez également une compatibilité ascendante pour un coût minime. Ce qui est toujours bon

ZoDB + ZEO implémente une base de données basée sur les révisions avec une restauration complète à n’importe quel moment. Va vérifier.

Mauvaise partie: Zope est lié.

Une fois qu'un objet est enregistré dans une base de données, nous pouvons le modifier autant de fois que nécessaire. Si nous voulons savoir combien de fois un objet est modifié, nous devons appliquer ce concept de gestion des versions.

Chaque fois que nous utilisons le versioning, alors hibernate insère le numéro de version à zéro, chaque fois que l'objet est enregistré pour la première fois dans la base de données. Ultérieurement, Hibernate incrémente cette version no by one automatiquement chaque fois qu'une modification est apportée à cet objet particulier. Pour utiliser ce concept de gestion de version, nous avons besoin des deux modifications suivantes dans notre application

Add one property of type int in our pojo class.

In hibernate mapping file, add an element called version soon after id element

Je ne suis pas sûr d'avoir le même problème, mais j'ai demandé un grand nombre de modifications "proposées" à l'ensemble de données actuel (avec des propositions chaînées, c'est-à-dire, proposition sur proposition).

Pensez à créer des branches dans le contrôle de code mais pour les tables de base de données.

Nous voulions également un journal historique, mais c'était le facteur le moins important - le principal problème était de gérer les propositions de changement pouvant durer six mois ou plus, à mesure que l'entreprise envisageait l'approbation du changement et se préparait à la mise en œuvre du changement. .

L'idée est que les utilisateurs peuvent charger une modification et commencer à créer, éditer, supprimer l'état actuel des données sans appliquer réellement ces modifications. Annulez les modifications apportées ou annulez l'intégralité de la modification.

La seule façon dont j'ai pu réaliser cela est d'avoir un ensemble de champs communs sur mes tables versionnées:

ID racine : obligatoire - définissez une fois la clé primaire lors de la création de la première version d'un enregistrement. Cela représente la clé primaire à travers le temps et est copié dans chaque version de l'enregistrement. Vous devez tenir compte de l'ID racine lorsque vous nommez des colonnes de relation (par exemple, PARENT_ROOT_ID au lieu de PARENT_ID). L'ID racine étant également la clé primaire de la version initiale, il est possible de créer des clés étrangères par rapport à la clé primaire réelle. La ligne souhaitée sera déterminée par les filtres de version définis ci-dessous.

Identifiant de modification : obligatoire - chaque enregistrement est créé, mis à jour, supprimé via une modification

.

ID copié à partir : Nullable - null indique un enregistrement nouvellement créé, non-null indique l'ID d'enregistrement à partir duquel cette ligne a été clonée lors de la mise à jour

Date / heure d'entrée en vigueur : Nullable - null indique l'enregistrement proposé, non-null indique le moment où l'enregistrement est devenu courant. Malheureusement, un index unique ne peut pas être placé sur l'ID racine / Effective de, car il peut y avoir plusieurs valeurs NULL pour tout ID racine. (Sauf si vous souhaitez vous limiter à un seul changement proposé par enregistrement)

Date / heure d'effet : Nullable - null indique le type actuel / proposé, non nul indique le moment où il est devenu historique. Non requis techniquement, mais permet d’accélérer les requêtes pour retrouver les données actuelles. Ce champ peut être corrompu par des modifications manuelles, mais peut être reconstitué à partir de la date / heure de début effective si cela se produit.

Indicateur de suppression : Boolean: définissez ce paramètre sur true lorsqu'il est proposé de supprimer l'enregistrement dès qu'il devient courant. Lorsque les suppressions sont validées, leur date / heure d’entrée en vigueur est définie sur la même valeur que la date / heure d’entrée en vigueur, en les filtrant du jeu de données actuel.

La requête permettant d'obtenir l'état actuel des données en fonction d'une modification serait;

SELECT * FROM table WHERE (CHANGE_ID IN :ChangeId OR (EFFECTIVE_FROM <= :Now AND (EFFECTIVE_TO IS NULL OR EFFECTIVE_TO > :Now) AND ROOT_ID NOT IN (SELECT ROOT_ID FROM table WHERE CHANGE_ID IN :ChangeId)))

(Le filtrage des multiples de modification à la modification est effectué en dehors de cette requête).

La requête permettant d'obtenir l'état actuel des données à un moment donné serait;

SELECT * FROM table WHERE EFFECTIVE_FROM <= :Now AND (EFFECTIVE_TO IS NULL OR EFFECTIVE_TO > :Now)

Index communs créés sur (ROOT_ID, EFFECTIVE_FROM), (EFFECTIVE_FROM, EFFECTIVE_TO) et (CHANGE_ID).

Si quelqu'un connaît une meilleure solution, j'aimerais en entendre parler.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top