web-dev-qa-db-fra.com

Comment stocker des données historiques

Certains collègues et moi avons engagé un débat sur la meilleure façon de stocker des données historiques. Actuellement, pour certains systèmes, j'utilise une table distincte pour stocker les données historiques et je conserve une table originale pour l'enregistrement actif en cours. Alors, disons que j'ai la table FOO. Dans mon système, tous les enregistrements actifs iront dans FOO et tous les enregistrements historiques dans FOO_Hist. De nombreux champs de FOO peuvent être mis à jour par l'utilisateur, je souhaite donc garder un compte-rendu précis de tout ce qui est mis à jour. FOO_Hist contient exactement les mêmes champs que FOO, à l'exception d'un HIST_ID auto-incrémenté. Chaque fois que FOO est mis à jour, j'effectue une instruction insert dans FOO_Hist similaire à celle-ci: insert into FOO_HIST select * from FOO where id = @id

Mon collègue dit que c'est une mauvaise conception parce que je ne devrais pas avoir une copie exacte d'un tableau pour des raisons historiques mais que je devrais simplement insérer un autre enregistrement dans le tableau actif avec un drapeau indiquant qu'il s'agit d'un but historique.

Existe-t-il une norme pour gérer le stockage des données historiques? Il me semble que je ne veux pas encombrer tous mes enregistrements actifs avec tous mes enregistrements historiques dans le même tableau, sachant que cela pourrait représenter plus d'un million d'enregistrements (je pense à long terme).

Comment vous ou votre entreprise gérez-vous cela?

J'utilise MS SQL Server 2008, mais j'aimerais garder la réponse générique et arbitraire de tout SGBD.

134
Aaron

La prise en charge des données historiques directement au sein d'un système opérationnel rendra votre application beaucoup plus complexe qu'elle ne le serait autrement. En général, je ne recommanderais pas de le faire sauf si vous avez une obligation absolue de manipuler les versions historiques d'un enregistrement dans le système. 

Si vous regardez de plus près, la plupart des exigences relatives aux données historiques entrent dans l'une des deux catégories suivantes:

  • Journalisation d'audit: Ceci est mieux réalisé avec des tables d'audit. Il est relativement facile d'écrire un outil qui génère des scripts pour créer des tables et des déclencheurs de journaux d'audit en lisant des métadonnées à partir du dictionnaire de données système. Ce type d’outil peut être utilisé pour moderniser la journalisation d’audit sur la plupart des systèmes. Vous pouvez également utiliser ce sous-système pour la capture de données modifiée si vous souhaitez implémenter un entrepôt de données (voir ci-dessous).

  • Rapports historiques: / Rapports sur l'état historique, les positions «en tant que» ou les rapports analytiques au fil du temps. Il peut être possible de satisfaire aux exigences de rapports historiques simples en interrogeant des tables de journalisation d'audit du type décrit ci-dessus. Si vos exigences sont plus complexes, il peut s'avérer plus économique de mettre en place un magasin de données pour le reporting plutôt que d'essayer d'intégrer l'historique directement dans le système opérationnel.

    Les dimensions qui changent lentement sont de loin le mécanisme le plus simple pour suivre et interroger l'état de l'historique, et une grande partie du suivi de l'historique peut être automatisé. Les gestionnaires génériques ne sont pas si difficiles à écrire. Généralement, les rapports historiques ne doivent pas nécessairement utiliser des données à la minute près, un mécanisme d'actualisation par lots convient normalement. Cela permet de garder votre architecture de base et votre système de reporting relativement simples.

Si vos exigences appartiennent à l'une de ces deux catégories, il est probablement préférable de ne pas stocker les données historiques dans votre système opérationnel. Séparer la fonctionnalité de l'historique dans un autre sous-système nécessitera probablement moins d'effort général et produira des bases de données transactionnelles et d'audit/de reporting qui fonctionnent beaucoup mieux pour l'objectif recherché.

Je ne pense pas qu'il y ait une manière standard de le faire, mais j'ai pensé introduire une méthode possible. Je travaille dans Oracle et dans notre infrastructure d'application Web interne qui utilise XML pour stocker les données d'application.

Nous utilisons ce que l’on appelle un modèle maître-détail qui, à son sens le plus simple, consiste à:

Master Table par exemple, appelé Widgets contenant souvent uniquement un identifiant. Contiendra souvent des données qui ne changeront pas dans le temps/n’est pas historique.

Table Détail/Historique par exemple appelée Widget_Details contenant au moins:

  • ID - clé primaire. Détail/historique
  • MASTER_ID - par exemple, dans le cas présent 'WIDGET_ID', il s'agit du FK de l'enregistrement principal
  • START_DATETIME - horodatage indiquant le début de cette ligne de la base de données
  • END_DATETIME - horodatage indiquant la fin de cette ligne de la base de données
  • STATUS_CONTROL - La colonne à un seul caractère indique le statut de la ligne. "C" indique en cours, NULL ou "A" serait historique/archivé. Nous ne l'utilisons que parce que nous ne pouvons pas indexer avec END_DATETIME étant NULL
  • CREATED_BY_WUA_ID - stocke l'ID du compte à l'origine de la création de la ligne.
  • XMLDATA - stocke les données réelles

Donc, essentiellement, une entité commence par avoir 1 ligne dans le maître et 1 ligne dans le détail. Le détail ayant une date de fin NULL et STATUS_CONTROL de 'C' . Lorsqu'une mise à jour est effectuée, la ligne en cours est mise à jour pour indiquer END_DATETIME de l'heure actuelle et status_control est défini sur NULL (ou 'A' si vous le souhaitez). Une nouvelle ligne est créée dans la table de détail, toujours liée au même maître, avec status_control 'C', l'identifiant de la personne effectuant la mise à jour et les nouvelles données stockées dans la colonne XMLDATA.

C'est la base de notre modèle historique. La logique de création/mise à jour est gérée dans un package Oracle PL/SQL. Vous devez simplement transmettre à la fonction l'ID actuel, votre ID utilisateur et les nouvelles données XML. En interne, elle met à jour/insère toutes les lignes pour représenter cet élément dans le modèle historique. . Les heures de début et de fin indiquent quand cette ligne de la table est active.

Le stockage est bon marché, nous ne SUPPRIMONS généralement pas les données et préférons conserver une trace d'audit. Cela nous permet de voir à quoi ressemblent nos données à un moment donné. En indexant status_control = 'C' ou en utilisant une vue, l'encombrement ne pose pas vraiment de problème. Il est évident que vos requêtes doivent prendre en compte, vous devez toujours utiliser la version actuelle (NULL end_datetime et status_control = 'C') d'un enregistrement.

34

Je pense que votre approche est correcte. La table historique doit être une copie de la table principale sans index. Assurez-vous également que l'horodatage de mise à jour est présent dans la table. 

Si vous essayez assez rapidement l’autre approche, vous rencontrerez des problèmes:

  • frais généraux de maintenance 
  • plus de drapeaux en sélection
  • ralentissement des requêtes
  • croissance des tableaux, des index
8
Alexander

Dans SQL Server 2016 et supérieur , il existe une nouvelle fonctionnalité appelée Tables temporelles qui vise à résoudre ce problème avec effort minimal du développeur . Le concept de table temporelle est similaire à la capture de données de changement (CDC), à la différence que la table temporelle a résumé la plupart des tâches que vous deviez effectuer manuellement si vous utilisiez CDC.

2
Benjamin

cette question est plutôt ancienne mais les gens travaillent encore sur cette question. donc si vous utilisez Oracle, le flashback Oracle pourrait vous intéresser: http://docs.Oracle.com/cd/B28359_01/appdev.111/b28424/adfns_flashback.htm

1
Jakob

Modifier la capture de données: https://docs.Microsoft.com/en-us/sql/relational-databases/track-changes/about-change-data-capture-sql-server?view=sql-server-2017

Il est pris en charge dans SQL Server 2008 R2 et peut avoir été pris en charge dans SQL Server 2008.

1
costa

Vous pouvez utiliser la fonctionnalité d'audit MSSQL Server. A partir de la version SQL Server 2012, vous trouverez cette fonctionnalité dans toutes les éditions:

http://technet.Microsoft.com/en-us/library/cc280386.aspx

0
Tomas Kubes

La vraie question est de savoir si vous devez utiliser des données historiques et des données actives pour la création de rapports. Si tel est le cas, conservez-les dans une seule table, partitionnez et créez une vue pour les enregistrements actifs à utiliser dans les requêtes actives. Si vous avez seulement besoin de les regarder de temps en temps (pour rechercher des problèmes juridiques ou autres), placez-les dans un tableau séparé.

0
HLGEM

Une autre option consiste à archiver les données opérationnelles sur une base quotidienne. La plupart des moteurs de base de données prennent en charge l'extraction des données dans une archive .

Fondamentalement, l’idée est de créer un travail Windows ou CRON planifié qui

  1. détermine les tables actuelles dans la base de données opérationnelle
  2. sélectionne toutes les données de chaque table dans un fichier CSV ou XML
  3. compresse les données exportées dans un fichier Zip, de préférence avec l'horodatage de la génération dans le nom du fichier pour un archivage plus facile.

De nombreux moteurs de base de données SQL sont livrés avec un outil qui peut être utilisé à cette fin. Par exemple, lors de l'utilisation de MySQL sous Linux, la commande suivante peut être utilisée dans un travail CRON pour planifier l'extraction:

mysqldump --all-databases --xml --lock-tables=false -ppassword | gzip -c | cat > /media/bak/servername-$(date +%Y-%m-%d)-mysql.xml.gz
0
Michael

Je voulais juste ajouter une option que j'ai commencé à utiliser parce que j'utilisais Azure SQL et que le problème des tables multiples était beaucoup trop lourd pour moi. J'ai ajouté un déclencheur d'insertion/mise à jour/suppression sur ma table, puis converti le changement avant/après en json à l'aide de la fonction "FOR JSON AUTO". 

 SET @beforeJson = (SELECT * FROM DELETED FOR JSON AUTO)
SET @afterJson = (SELECT * FROM INSERTED FOR JSON AUTO)

Cela retourne une représentation JSON de l'enregistrement avant/après le changement. Je stocke ensuite ces valeurs dans une table d'historique avec un horodatage indiquant le moment où le changement a eu lieu (je stocke également l'ID pour l'enregistrement en cours de préoccupation). En utilisant le processus de sérialisation, je peux contrôler la façon dont les données sont remises en cas de modification du schéma. 

J'ai appris cela par ce lien ici

0
JakeHova

Vous pouvez créer une vue matérialisée/indexée sur la table. Selon vos besoins, vous pouvez effectuer une mise à jour complète ou partielle des vues. Veuillez voir ceci pour créer mview et vous connecter. Comment créer des vues matérialisées dans SQL Server?

0
Manu

Vous pouvez simplement partitionner les tables non?

"Stratégies de tables et d’index partitionnés avec SQL Server 2008 Lorsqu'une table de base de données atteint des centaines de giga-octets ou plus, il peut s'avérer plus difficile de charger de nouvelles données, de supprimer des anciennes données et de conserver des index. Même si les données à charger ou à supprimer peuvent être très volumineuses, les opérations INSERT et DELETE sur la table sont peu pratiques. Le logiciel de base de données Microsoft SQL Server 2008 fournit un partitionnement de table pour rendre ces opérations plus maniable."

0
clyc

Je connais ce vieil article mais je voulais juste ajouter quelques points . La norme pour de tels problèmes est ce qui fonctionne le mieux dans cette situation. comprendre la nécessité d’un tel stockage, et l’utilisation potentielle des données de suivi des historiques/audits/modifications est très importante. 

Audit (objectif de sécurité) : Utilisez une table commune pour toutes vos tables pouvant être auditées. définir une structure pour stocker le nom de la colonne, avant et après la valeur des champs. 

Archive/Historical : pour des cas tels que le suivi de l'adresse précédente, du numéro de téléphone, etc., la création d'une table distincte est préférable si votre schéma de table de transaction active ne change pas de manière significative structure). si vous anticipez la normalisation des tables, l’ajout/la suppression de colonnes de types de données, modifiez vos données d’historique au format XML. définir une table avec les colonnes suivantes (ID, Date, Version du schéma, XMLData). cela gérera facilement les changements de schéma. mais vous devez faire face à xml et cela pourrait introduire un niveau de complication pour la récupération des données.

0
Danny D