Je suis sûr que beaucoup d'applications, d'applications critiques, de banques, etc. le font quotidiennement.
L'idée derrière tout cela est:
- toutes les lignes doivent avoir un historique
- tous les liens doivent rester cohérents
- il devrait être facile de faire des demandes pour obtenir des colonnes "actuelles"
- les clients qui ont acheté des objets obsolètes devraient toujours voir ce qu'ils ont acheté même si ce produit ne fait plus partie du catalogue
etc.
Voici ce que je veux faire et je vais vous expliquer les problèmes auxquels je suis confronté.
Toutes mes tables auront ces colonnes:
id
id_origin
date of creation
start date of validity
start end of validity
Et voici les idées pour les opérations CRUD:
- create = insérer une nouvelle ligne avec
id_origin
=id
,date of creation
= now,start date of validity
= now,end date of validity
= null (= signifie que c'est l'enregistrement actif en cours) - mise à jour =
- lire = lire tous les enregistrements avec
end date of validity
== null - mettre à jour l'enregistrement "actuel"
end date of validity
= null avecend date of validity
= maintenant - en créer un nouveau avec les nouvelles valeurs, et
end date of validity
= null (= signifie qu'il s'agit de l'enregistrement actif en cours)
- lire = lire tous les enregistrements avec
- delete = mettre à jour l'enregistrement "actuel"
end date of validity
= null avecend date of validity
= maintenant
Voici donc mon problème: avec les associations plusieurs-à-plusieurs. Prenons un exemple avec des valeurs:
- Tableau A (id = 1, id_origin = 1, start = now, end = null)
- Table A_B (début = maintenant, fin = null, id_A = 1, id_B = 48)
- Tableau B (id = 48, id_origin = 48, start = now, end = null)
Maintenant, je veux mettre à jour la table A, enregistrer id = 1
- Je marque l'enregistrement id = 1 avec fin = maintenant
J'insère une nouvelle valeur dans la table A et ... putain j'ai perdu ma relation A_B à moins que je ne reproduise la relation aussi ... cela finirait par une table:
Tableau A (id = 1, id_origin = 1, start = now, end = now + 8mn)
- Tableau A (id = 2, id_origin = 1, start = now + 8mn, end = null)
- Table A_B (début = maintenant, fin = null, id_A = 1, id_B = 48)
- Table A_B (début = maintenant, fin = null, id_A = 2, id_B = 48)
- Tableau B (id = 48, id_origin = 48, start = now, end = null)
Et ... eh bien j'ai un autre problème: la relation A_B: dois-je marquer (id_A = 1, id_B = 48) comme obsolète ou non (A - id = 1 est obsolète, mais pas B - 48)?
Comment y faire face?
Je dois concevoir cela à grande échelle: produits, partenaires, etc.
Quelle est votre expérience à ce sujet? Comment feriez-vous (comment avez-vous fait)?
-- Éditer
J'ai trouvé cet article très intéressant , mais il ne traite pas correctement de «l'obsolescence en cascade» (= ce que je demande en fait)
la source
Réponses:
Il n'est pas clair pour moi si ces exigences sont à des fins d'audit ou simplement pour une simple référence historique, comme avec le CRM et les paniers d'achat.
Dans les deux cas, envisagez d'avoir une table principale et main_archive pour chaque zone principale où cela est nécessaire. "Main" n'aura que les entrées actuelles / actives tandis que "main_archive" aura une copie de tout ce qui entre dans main. L'insertion / mise à jour dans main_archive peut être un déclencheur de l'insertion / mise à jour dans main. Les suppressions contre main_archive peuvent alors s'exécuter sur une plus longue période de temps, si jamais.
Pour les problèmes référentiels tels que Cust X a acheté le produit Y, le moyen le plus simple de résoudre votre problème référentiel de cust_archive -> product_archive est de ne jamais supprimer les entrées de product_archive. En règle générale, le taux de désabonnement devrait être beaucoup plus faible dans ce tableau afin que la taille ne soit pas trop préoccupante.
HTH.
la source
LP_
, et chaque table importante a un équivalentLH_
, avec des déclencheurs insérant des lignes historiques lors de l'insertion, de la mise à jour, de la suppression. Cela ne fonctionne pas dans tous les cas, mais c'est un modèle solide pour ce que je fais.UNION
vue, ce qui vous permet de faire des choses sympas comme appliquer une contrainte unique sur les enregistrements actuels et historiques.id
" et "id_ref
".id_ref
est une référence à l'idée réelle de la table. Exemple:person
etperson_h
. dansperson_h
j'ai "id
", et "id_ref
" oùid_ref
est lié à 'person.id
' donc je peux avoir plusieurs lignes avec le mêmeperson.id
(= quand une ligne deperson
est modifiée) etid
toutes mes tables sont autoinc.Cela a un certain chevauchement avec la programmation fonctionnelle; spécifiquement le concept d'immuabilité.
Vous avez une table appelée
PRODUCT
et une autre appeléePRODUCTVERSION
ou similaire. Lorsque vous modifiez un produit, vous ne faites pas de mise à jour, vous insérez simplement une nouvellePRODUCTVERSION
ligne. Pour obtenir la dernière version, vous pouvez indexer la table par numéro de version (desc), horodatage (desc), ou vous pouvez avoir un indicateur (LatestVersion
).Maintenant, si vous avez quelque chose qui fait référence à un produit, vous pouvez décider du tableau vers lequel il pointe. Pointe-t-il vers l'
PRODUCT
entité (se réfère toujours à ce produit) ou vers l'PRODUCTVERSION
entité (se réfère uniquement à cette version du produit)?Ça se complique. Et si vous avez des photos du produit? Ils doivent pointer vers la table des versions, car ils pourraient être modifiés, mais dans de nombreux cas, ils ne le feront pas et vous ne voulez pas dupliquer les données inutilement. Cela signifie que vous avez besoin d'une
PICTURE
table et d'unePRODUCTVERSIONPICTURE
relation plusieurs-à-plusieurs.la source
J'ai implémenté toutes les choses d' ici avec 4 champs qui sont sur toutes mes tables:
Chaque fois qu'un enregistrement doit être modifié, je le duplique, marque l' enregistrement dupliqué comme "ancien" =
date_validity_end=NOW()
et l'actuel comme le bondate_validity_start=NOW()
etdate_validity_end=NULL
.L'astuce concerne les relations plusieurs à plusieurs et une à plusieurs: cela fonctionne sans les toucher! Il s'agit de requêtes plus complexes: pour interroger un enregistrement à une date précise (= pas maintenant), j'ai pour chaque jointure, et pour la table principale, d'ajouter ces contraintes:
Donc, avec des produits et des attributs (relation plusieurs à plusieurs):
la source
Que dis-tu de ça? Cela semble simple et assez efficace pour ce que j'ai fait dans le passé. Dans votre tableau "historique", utilisez un PK différent. Ainsi, votre champ "CustomerID" est le PK dans votre table Customer, mais dans la table "history", votre PK est "NewCustomerID". "CustomerID" devient juste un autre champ en lecture seule. Cela laisse "CustomerID" inchangé dans l'historique et toutes vos relations restent intactes.
la source