Débat sur la conception :quelles sont les bonnes façons de stocker et de manipuler des objets versionnés ?[fermé]

StackOverflow https://stackoverflow.com/questions/11689

Question

Je laisse intentionnellement cela assez vague au début.Je recherche davantage la discussion et les questions importantes que les réponses concrètes.

Je suis en train de concevoir une application qui fait quelque chose comme la gestion de portefeuille.Le design que j'ai jusqu'à présent est

  • Problème:un problème qui doit être résolu
  • Solution:une proposition de solution à un ou plusieurs problèmes
  • Relation:une relation entre deux problèmes, deux solutions, ou un problème et une solution.Décomposé en :
    • Parent-enfant - une sorte de catégorisation/hiérarchie arborescente
    • Chevauchement - la mesure dans laquelle deux solutions ou deux problèmes abordent réellement le même concept
    • Adresses - la mesure dans laquelle un problème répond à une solution

Ma question porte sur la nature temporelle de ces choses.Les problèmes surgissent, puis disparaissent.Les solutions ont une date de résolution prévue, mais celle-ci pourrait être modifiée au fur et à mesure de leur développement.Le degré d'une relation peut changer au fil du temps, à mesure que les problèmes et les solutions évoluent.

Alors, la question :Quelle est la meilleure conception pour la gestion des versions de ces éléments afin que je puisse obtenir à la fois une perspective actuelle et historique de mon portefeuille ?

Plus tard:je devrais peut-être en faire une question plus spécifique, même si la réponse de @Eric Beard vaut la peine.

J'ai envisagé trois conceptions de bases de données.J'en parlerai suffisamment pour montrer leurs inconvénients.Ma question est:lequel choisir, ou pouvez-vous penser à quelque chose de mieux ?

1:Les problèmes (et séparément, les solutions) sont auto-référentiels dans la gestion des versions.

table problems
  int id | string name | text description | datetime created_at | int previous_version_id

  foreign key previous_version_id -> problems.id

C'est problématique car chaque fois que je veux une nouvelle version, je dois dupliquer toute la ligne, y compris cette longue description colonne.

2 :Créez un nouveau type de relation :Version.

table problems
  int id | string name | text description | datetime created_at

Cela déplace simplement la relation des tables Problèmes et Solutions vers la table Relations.Même problème de duplication, mais peut-être un peu "plus propre" puisque j'ai déjà un concept abstrait de Relation.

3 :Utilisez une structure plus proche de celle de Subversion ;déplacez tous les attributs de problème et de solution dans un tableau séparé et versionnez-les.

table problems
  int id

table attributes
  int id | int thing_id | string thing_type | string name | string value | datetime created_at | int previous_version_id

  foreign key (thing_id, thing_type) -> problems.id or solutions.id
  foreign key previous_version_id -> attributes.id

Cela signifie que pour charger la version actuelle d'un problème ou d'une solution, je dois récupérer toutes les versions de l'attribut, les trier par date, puis utiliser la plus récente.Ce n'est peut-être pas terrible.Ce qui me semble vraiment mauvais, c'est que je ne peux pas vérifier ces attributs dans la base de données.Que value la colonne doit être en texte libre.je peux faire le name colonne une référence dans un espace séparé attribute_names table qui a un type colonne, mais ce n'est pas le cas forcer le type correct dans le attributes tableau.

plus tard encore :réponse aux commentaires de @Eric Beard sur les clés étrangères multi-tables :

Hélas, ce que j'ai décrit est simpliste :il n'y a que deux types de choses (problèmes et solutions).J'ai en fait environ 9 ou 10 types de choses différents, j'aurais donc 9 ou 10 colonnes de clés étrangères dans le cadre de votre stratégie.Je voulais utiliser l'héritage à table unique, mais les Objets ont si peu de points communs que ce serait extrêmement inutile de les combiner en une seule table.

Était-ce utile?

La solution

Hmm, ça ressemble un peu à ce site...

En ce qui concerne la conception d'une base de données, un système de gestion de versions comme SVN, dans lequel vous n'effectuez jamais de mises à jour, vous insérez simplement (avec un numéro de version) lorsque les choses changent, pourrait être ce dont vous avez besoin.C’est ce qu’on appelle MVCC, Multi-Value Concurrency Control.Un wiki en est un autre bon exemple.

Autres conseils

@Gaïus

foreign key (thing_id, thing_type) -> problems.id or solutions.id

Soyez prudent avec ce type de clés étrangères « multidirectionnelles ».Mon expérience a montré que les performances des requêtes souffrent considérablement lorsque votre condition de jointure doit vérifier le type avant de déterminer sur quelle table se joindre.Cela ne semble pas aussi élégant mais nullable

problem_id and solution_id 

fonctionnera beaucoup mieux.

Bien entendu, les performances des requêtes souffriront également avec une conception MVCC lorsque vous devrez ajouter la vérification pour obtenir la dernière version d'un enregistrement.Le compromis est que vous n’avez jamais à vous soucier des conflits avec les mises à jour.

Comment pensez-vous cela :

problèmes de table
int id | Nom de la chaîne | Description du texte | DateTime Created_at

problèmes de table_révisions
INT REVISION | int id | Nom de la chaîne | Description du texte | DateTime Created_at
identifiant de clé étrangère -> problèmes.id

Avant les mises à jour, vous devez effectuer une insertion supplémentaire dans la table de révision.Cet insert supplémentaire est rapide, mais c'est ce que vous devez payer

  1. accès efficace à la version actuelle - sélectionnez les problèmes comme d'habitude
  2. un schéma intuitif et proche de la réalité que vous souhaitez modéliser
  3. les jointures entre les tables de votre schéma restent efficaces
  4. en utilisant un numéro de révision par transaction commerciale, vous pouvez effectuer une gestion des versions sur les enregistrements de table comme SVN le fait sur les fichiers.

Je suppose qu'il y a

Option 4 :l'hybride

Déplacez les attributs d'objet communs dans une table à héritage unique, puis ajoutez un custom_attributes tableau.Cela simplifie les clés étrangères, réduit la duplication et permet une flexibilité.Cela ne résout pas les problèmes de sécurité des types pour les attributs supplémentaires.Cela ajoute également un peu de complexité puisqu'il existe désormais deux manières pour une chose d'avoir un attribut.

Si description et d'autres champs volumineux restent dans la table Things, mais cela ne résout pas non plus le problème de l'espace de duplication.

table things
  int id | int type | string name | text description | datetime created_at | other common fields...
  foreign key type -> thing_types.id

table custom_attributes
  int id | int thing_id | string name | string value
  foreign key thing_id -> things.id

C'est une bonne idée de choisir une structure de données qui permet de répondre facilement aux questions courantes que vous posez au modèle.Il est fort probable que le poste actuel vous intéresse la plupart du temps.À l’occasion, vous souhaiterez explorer l’historique pour trouver des problèmes et des solutions particuliers.

J'aurais des tableaux pour le problème, la solution et la relation qui représentent la position actuelle.Il y aurait aussi un problem_history, solution_history, etc.Il s'agirait de tables enfants du problème mais contiendraient également des colonnes supplémentaires pour VersionNumber et EffectiveDate.La clé serait (ProblemId, VersionNumber).

Lorsque vous mettez à jour un problème, vous écrivez les anciennes valeurs dans le problem_history tableau.Des requêtes ponctuelles dans le temps sont donc possibles car vous pouvez sélectionner le problem_history enregistrement valide à une date donnée.

Là où je l'ai déjà fait, j'ai également créé une vue sur UNION problem et problem_history car cela est parfois utile dans diverses requêtes.

L'option 1 rend difficile l'interrogation de la situation actuelle, car toutes vos données historiques sont mélangées à vos données actuelles.

L'option 3 va être mauvaise pour les performances des requêtes et désagréable à coder car vous accéderez à de nombreuses lignes pour ce qui ne devrait être qu'une simple requête.

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