Speichern historischer Daten [geschlossen]


165

Einige Mitarbeiter und ich haben eine Debatte darüber geführt, wie historische Daten am besten gespeichert werden können. Derzeit verwende ich für einige Systeme eine separate Tabelle zum Speichern historischer Daten und behalte eine Originaltabelle für den aktuellen aktiven Datensatz. Nehmen wir also an, ich habe Tisch FOO. Unter meinem System werden alle aktiven Datensätze in FOO und alle historischen Datensätze in FOO_Hist gespeichert. Viele verschiedene Felder in FOO können vom Benutzer aktualisiert werden, daher möchte ich ein genaues Konto über alles auf dem neuesten Stand halten. FOO_Hist enthält genau die gleichen Felder wie FOO, mit Ausnahme einer automatisch inkrementierenden HIST_ID. Jedes Mal, wenn FOO aktualisiert wird, führe ich eine Einfügeanweisung in FOO_Hist aus, ähnlich : insert into FOO_HIST select * from FOO where id = @id.

Mein Kollege sagt, dass dies ein schlechtes Design ist, da ich aus historischen Gründen keine exakte Kopie einer Tabelle haben sollte und einfach einen weiteren Datensatz in die aktive Tabelle einfügen sollte, wobei ein Flag angibt, dass dies für historische Zwecke ist.

Gibt es einen Standard für den Umgang mit der Speicherung historischer Daten? Es scheint mir, dass ich meine aktiven Aufzeichnungen nicht mit all meinen historischen Aufzeichnungen in derselben Tabelle überladen möchte, wenn man bedenkt, dass es weit über eine Million Aufzeichnungen sein können (ich denke langfristig).

Wie gehen Sie oder Ihr Unternehmen damit um?

Ich verwende MS SQL Server 2008, möchte aber die Antwort generisch und willkürlich für jedes DBMS halten.


Antworten:


81

Durch die Unterstützung historischer Daten direkt in einem Betriebssystem wird Ihre Anwendung viel komplexer als sonst. Im Allgemeinen würde ich dies nur empfehlen, wenn Sie die historischen Versionen eines Datensatzes im System manipulieren müssen.

Wenn Sie genau hinschauen, fallen die meisten Anforderungen an historische Daten in eine von zwei Kategorien:

  • Überwachungsprotokollierung: Dies ist besser mit Überwachungstabellen möglich. Es ist ziemlich einfach, ein Tool zu schreiben, das Skripte zum Erstellen von Überwachungsprotokolltabellen und -auslösern generiert, indem Metadaten aus dem Systemdatenwörterbuch gelesen werden. Diese Art von Tool kann verwendet werden, um die Audit-Protokollierung auf den meisten Systemen nachzurüsten. Sie können dieses Subsystem auch für die geänderte Datenerfassung verwenden, wenn Sie ein Data Warehouse implementieren möchten (siehe unten).

  • Historische Berichterstattung: Berichterstattung über den historischen Zustand, den Stand der Dinge oder die analytische Berichterstattung im Zeitverlauf. Es kann möglich sein, einfache historische Berichtsanforderungen zu erfüllen, indem Audit-Protokollierungstabellen der oben beschriebenen Art abgefragt werden. Wenn Sie komplexere Anforderungen haben, ist es möglicherweise wirtschaftlicher, einen Data Mart für die Berichterstellung zu implementieren, als zu versuchen, den Verlauf direkt in das Betriebssystem zu integrieren.

    Sich langsam ändernde Dimensionen sind bei weitem der einfachste Mechanismus zum Verfolgen und Abfragen des historischen Status, und ein Großteil der Verlaufsverfolgung kann automatisiert werden. Generische Handler sind nicht so schwer zu schreiben. Im Allgemeinen müssen für die historische Berichterstellung keine aktuellen Daten verwendet werden. Daher ist ein Stapelaktualisierungsmechanismus normalerweise in Ordnung. Dies hält Ihre Kern- und Berichtssystemarchitektur relativ einfach.

Wenn Ihre Anforderungen in eine dieser beiden Kategorien fallen, ist es wahrscheinlich besser, historische Daten nicht in Ihrem Betriebssystem zu speichern. Die Aufteilung der historischen Funktionalität in ein anderes Subsystem wird wahrscheinlich insgesamt weniger Aufwand bedeuten und Transaktions- und Prüf- / Berichtsdatenbanken erstellen, die für den beabsichtigten Zweck viel besser funktionieren.


Ich glaube ich sehe was du sagst. Also habe ich mit meiner FOO_Hist-Tabelle wirklich eine Audit-Tabelle erstellt. Anstatt beim Update einen Trigger zum Einfügen in die Audit-Tabelle zu verwenden, habe ich einfach eine Anweisung im Programm ausgeführt. Ist das korrekt?
Aaron

6
Ja schon. Es ist jedoch besser, diese Art der Überwachungsprotokollierung mit Triggern durchzuführen. Die Trigger stellen sicher, dass alle Änderungen (einschließlich manueller Datenkorrekturen) in den Überwachungsprotokollen aufgezeichnet werden. Wenn Sie mehr als 10 bis 20 Tabellen prüfen müssen, ist es wahrscheinlich insgesamt schneller, ein Trigger-Generator-Tool zu erstellen. Wenn der Festplattenverkehr für die Überwachungsprotokolle ein Problem darstellt, können Sie die Überwachungsprotokolltabellen auf einem separaten Satz von Datenträgern ablegen.
ConcernedOfTunbridgeWells

42

Ich glaube nicht, dass es eine bestimmte Standardmethode gibt, aber ich dachte, ich würde eine mögliche Methode einführen. Ich arbeite in Oracle und unserem internen Webanwendungsframework, das XML zum Speichern von Anwendungsdaten verwendet.

Wir verwenden ein sogenanntes Master-Detail-Modell, das im einfachsten Fall besteht aus:

Master Table zum Beispiel wird Widgetsoft nur mit einer ID aufgerufen . Enthält häufig Daten, die sich im Laufe der Zeit nicht ändern / nicht historisch sind.

Detail- / Verlaufstabelle zum Beispiel Widget_Detailsmit mindestens:

  • ID - Primärschlüssel. Detail / historische ID
  • MASTER_ID - in diesem Fall beispielsweise 'WIDGET_ID' genannt, ist dies die FK zum Stammsatz
  • START_DATETIME - Zeitstempel, der den Beginn dieser Datenbankzeile angibt
  • END_DATETIME - Zeitstempel, der das Ende dieser Datenbankzeile angibt
  • STATUS_CONTROL - Einzelne Zeichenspalte zeigt den Status der Zeile an. 'C' gibt an, dass aktuell, NULL oder 'A' historisch / archiviert sind. Wir verwenden dies nur, weil wir nicht indizieren können, dass END_DATETIME NULL ist
  • CREATED_BY_WUA_ID - speichert die ID des Kontos, durch das die Zeile erstellt wurde
  • XMLDATA - speichert die tatsächlichen Daten

Im Wesentlichen beginnt eine Entität damit, dass 1 Zeile im Master und 1 Zeile im Detail vorhanden sind. Das Detail hat ein NULL-Enddatum und STATUS_CONTROL von 'C'. Wenn eine Aktualisierung erfolgt, wird die aktuelle Zeile so aktualisiert, dass END_DATETIME der aktuellen Zeit angezeigt wird, und status_control wird auf NULL gesetzt (oder 'A', falls bevorzugt). In der Detailtabelle, die immer noch mit demselben Master verknüpft ist, wird eine neue Zeile mit status_control 'C', der ID der Person, die die Aktualisierung vornimmt, und den neuen Daten, die in der Spalte XMLDATA gespeichert sind, erstellt.

Dies ist die Grundlage unseres historischen Modells. Die Logik zum Erstellen / Aktualisieren wird in einem Oracle PL / SQL-Paket behandelt, sodass Sie der Funktion einfach die aktuelle ID, Ihre Benutzer-ID und die neuen XML-Daten übergeben und intern alle Zeilen aktualisieren / einfügen, um diese im historischen Modell darzustellen . Die Start- und Endzeiten geben an, wann diese Zeile in der Tabelle aktiv ist.

Speicher ist billig, wir LÖSCHEN im Allgemeinen keine Daten und ziehen es vor, einen Prüfpfad zu führen. Auf diese Weise können wir sehen, wie unsere Daten zu einem bestimmten Zeitpunkt aussahen. Durch Indizieren von status_control = 'C' oder Verwenden einer Ansicht ist Unordnung nicht gerade ein Problem. Natürlich müssen Ihre Abfragen berücksichtigen, dass Sie immer die aktuelle Version (NULL end_datetime und status_control = 'C') eines Datensatzes verwenden sollten.


Hallo Chris, wenn du das machst, muss die ID (Primärschlüssel) geändert werden, oder? Wie wäre es mit der Beziehung mit einer anderen Tabelle, wenn sie von einer anderen verwendet wird?
Projo

@projo Die ID in Ihrer Mastertabelle ist die PK und konzeptionell die "PK" für jedes Konzept, mit dem Sie sich befassen. Die ID in der Detailtabelle ist die PK, um eine historische Version für den Master zu identifizieren (dies ist eine weitere Spalte im Detail). Beim Bilden von Beziehungen verweisen Sie häufig auf die wahre PK Ihres Konzepts (dh die ID in Ihrer Mastertabelle oder die Spalte MASTER_ID in Ihrem Detail) und verwenden STATUS_CONTROL = 'C', um sicherzustellen, dass Sie die aktuelle Version erhalten. Alternativ können Sie auf die Detail-ID verweisen, um etwas auf einen bestimmten Zeitpunkt zu beziehen.
Chris Cameron-Mills

Wir verwenden den gleichen Ansatz. Aber jetzt frage ich mich, ob es besser ist, nur START_DATETIME und nicht END_DATETIME
bat_ventzi

Einige Variationen meiner Erfahrung. Wenn Ihre Entität "beendet", dh archiviert oder gelöscht ist, können Sie tatsächlich keine Detaildatensätze mit der Statussteuerung "C" haben, dh keine aktuelle Zeile, obwohl Sie nicht wissen würden, wann dies passiert ist. Alternativ können Sie in der letzten Zeile eine end_datetime festlegen, und das Vorhandensein einer 'beendeten' 'C'-Zeile kann darauf hinweisen, dass die Entität jetzt gelöscht / archiviert ist. Schließlich könnten Sie dies durch eine andere Spalte darstellen, STATUS, die Sie wahrscheinlich bereits haben.
Chris Cameron-Mills

@ ChrisCameron-Mills Der von Ihnen vorgeschlagene Ansatz würde besser funktionieren, wenn wir eine Haupttabelle und eine einzelne Detailtabelle haben. Was ist, wenn die Detailtabelle von einigen anderen Tabellen abhängt, die sich ebenfalls im Laufe der Zeit ändern? Eine Möglichkeit besteht darin, ähnliche Spalten hinzuzufügen, um die Versionsversionierung all dieser Tabellen zu verfolgen. Aber wird das nicht zu komplex sein?
Chris

15

Ich denke, Ihr Ansatz ist richtig. Die historische Tabelle sollte eine Kopie der Haupttabelle ohne Indizes sein. Stellen Sie sicher, dass die Tabelle auch einen Aktualisierungszeitstempel enthält.

Wenn Sie den anderen Ansatz früh genug ausprobieren, treten Probleme auf:

  • Wartungsaufwand
  • Weitere Flags in Auswahl
  • Abfragen verlangsamen
  • Wachstum von Tabellen, Indizes

7

In SQL Server 2016 und höher gibt es eine neue Funktion namens Temporal Tables , mit der diese Herausforderung mit minimalem Aufwand des Entwicklers gelöst werden soll . Das Konzept der temporalen Tabelle ähnelt der CDC (Change Data Capture), mit dem Unterschied, dass die temporale Tabelle die meisten Dinge abstrahiert hat, die Sie manuell ausführen mussten, wenn Sie CDC verwendeten.


1

Ich wollte nur eine Option hinzufügen, die ich verwendet habe, weil ich Azure SQL verwende und die Sache mit mehreren Tabellen für mich viel zu umständlich war. Ich habe meiner Tabelle einen Trigger zum Einfügen / Aktualisieren / Löschen hinzugefügt und dann die Vorher / Nachher-Änderung mithilfe der Funktion "FOR JSON AUTO" in json konvertiert.

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

Dies gibt eine JSON-Darstellung für den Datensatz vor / nach der Änderung zurück. Ich speichere diese Werte dann in einer Verlaufstabelle mit einem Zeitstempel des Zeitpunkts, zu dem die Änderung aufgetreten ist (ich speichere auch die ID für den aktuellen besorgniserregenden Datensatz). Mithilfe des Serialisierungsprozesses kann ich steuern, wie Daten bei Änderungen am Schema nachgefüllt werden.

Das habe ich über diesen Link hier erfahren


0

Sie könnten einfach die Tabellen partitionieren, nein?

"Partitionierte Tabellen- und Indexstrategien mit SQL Server 2008 Wenn eine Datenbanktabelle auf Hunderte von Gigabyte oder mehr vergrößert wird, kann es schwieriger werden, neue Daten zu laden, alte Daten zu entfernen und Indizes zu verwalten. Nur die schiere Größe der Tabelle Dies führt dazu, dass solche Vorgänge viel länger dauern. Selbst die Daten, die geladen oder entfernt werden müssen, können sehr umfangreich sein, was INSERT- und DELETE-Vorgänge für die Tabelle unpraktisch macht. Die Microsoft SQL Server 2008-Datenbanksoftware bietet Tabellenpartitionierung, um solche Vorgänge besser verwalten zu können. "


Ja, ich kann die Tabellen partitionieren, aber ist das der Standard beim Umgang mit historischen Daten? Sollten historische Daten in dieselbe Tabelle aufgenommen werden wie aktive Daten? Dies sind die Fragen, die ich diskutieren wollte. Dies ist auch nicht willkürlich, da es sich um SQL Server 2008 handelt.
Aaron

0

Die eigentliche Frage ist, ob Sie historische Daten und aktive Daten zusammen für die Berichterstellung verwenden müssen. Wenn ja, halten Sie sie in einer Tabelle, partitionieren Sie sie und erstellen Sie eine Ansicht für aktive Datensätze, die in aktiven Abfragen verwendet werden sollen. Wenn Sie sie nur gelegentlich ansehen müssen (um frühere Probleme oder ähnliches zu untersuchen), legen Sie sie in eine separate Tabelle.


2
Ist es schwieriger, JOINzwei Tabellen in einigen historischen Berichten zu erstellen, oder ist es schwieriger, jede einzelne Tabelle einzufügen, zu aktualisieren / zu löschen, um historische Bedenken zu berücksichtigen? Tatsächlich würde ein Überwachungsprotokoll sogar aktuelle Daten in die Verlaufstabelle aufnehmen, sodass die aktuelle Tabelle nicht einmal in einem Bericht benötigt werden sollte.

0

Eine andere Möglichkeit besteht darin, die Betriebsdaten [täglich | stündlich | was auch immer] zu archivieren. Die meisten Datenbankmodule unterstützen das Extrahieren der Daten in ein Archiv .

Grundsätzlich besteht die Idee darin, einen geplanten Windows- oder CRON-Job zu erstellen, der

  1. bestimmt die aktuellen Tabellen in der Betriebsdatenbank
  2. Wählt alle Daten aus jeder Tabelle in eine CSV- oder XML-Datei aus
  3. Komprimiert die exportierten Daten in eine ZIP-Datei, vorzugsweise mit dem Zeitstempel der Generierung im Dateinamen, um die Archivierung zu vereinfachen.

Viele SQL-Datenbankmodule verfügen über ein Tool, das für diesen Zweck verwendet werden kann. Wenn Sie beispielsweise MySQL unter Linux verwenden, kann der folgende Befehl in einem CRON-Job verwendet werden, um die Extraktion zu planen:

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

2
Dies ist für historische Daten überhaupt nicht geeignet, da Aktualisierungen verloren gehen, wenn jemand einen Wert ändert und ihn innerhalb des Archivierungszyklus zurück ändert. Es gibt auch keine einfache Möglichkeit, die Änderungen an einer Entität im Laufe der Zeit zu betrachten oder eine Entität teilweise wiederherzustellen.
Sgoettschkes

0

Ich kenne diesen alten Beitrag, wollte aber nur ein paar Punkte hinzufügen. Der Standard für solche Probleme ist, was für die Situation am besten funktioniert. Das Verständnis der Notwendigkeit einer solchen Speicherung und der möglichen Verwendung der Verlaufs- / Prüfungs- / Änderungsverfolgungsdaten ist sehr wichtig.

Prüfung (Sicherheitszweck) : Verwenden Sie eine gemeinsame Tabelle für alle Ihre überprüfbaren Tabellen. Definieren Sie die Struktur zum Speichern des Spaltennamens vor und nach den Wertfeldern.

Archiv / Verlauf : Für Fälle wie das Verfolgen der vorherigen Adresse, Telefonnummer usw. ist das Erstellen einer separaten Tabelle FOO_HIST besser, wenn sich Ihr aktives Transaktionstabellenschema in Zukunft nicht wesentlich ändert (wenn Ihre Verlaufstabelle dieselbe Struktur haben muss). Wenn Sie eine Tabellennormalisierung, das Hinzufügen / Entfernen von Spalten durch Ändern des Datentyps erwarten, speichern Sie Ihre Verlaufsdaten im XML-Format. Definieren Sie eine Tabelle mit den folgenden Spalten (ID, Datum, Schemaversion, XMLData). Dadurch können Schemaänderungen problemlos verarbeitet werden. Sie müssen sich jedoch mit XML befassen, was zu einer Komplikation beim Abrufen von Daten führen kann.



Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.