Best Practices für Verlaufs- / Zeittabellen?

11

Angenommen, ich habe ein Objekt mit bestimmten Feldern, die ich verfolgen möchte, und bestimmten Feldern, die ich nicht verfolgen möchte. Aus Sicht der Normalisierung ist das folgende Schema in Ordnung:

CREATE TABLE MyObject AS (
    MyObjectId INT IDENTITY NOT NULL PRIMARY KEY,
    MyObjectField1 VARCHAR(100) NOT NULL,
    MyObjectField2 VARCHAR(100) NOT NULL,
    MyObjectField3 VARCHAR(100) NOT NULL,
    MyObjectTrackedField1 VARCHAR(100) NOT NULL,
    MyObjectTrackedField2 VARCHAR(100) NOT NULL,
    MyObjectTrackedField3 VARCHAR(100) NOT NULL,
)
CREATE TABLE MyObjectHistory AS (
    MyObjectHistoryId INT IDENTITY NOT NULL PRIMARY KEY,
    MyObjectId INT NOT NULL FOREIGN KEY REFERENCES MyObject(MyObjectId),
    MyObjectTrackedField1 VARCHAR(100) NOT NULL,
    MyObjectTrackedField2 VARCHAR(100) NOT NULL,
    MyObjectTrackedField3 VARCHAR(100) NOT NULL,
)

Dabei enthält MyObjectHistory die nachverfolgten Felder für alle außer der neuesten Version. Oder sollten sich alle nachverfolgten Felder in einer Tabelle befinden und alle Revisionen einschließlich der neuesten in dieser Tabelle enthalten sein, wie in:

CREATE TABLE MyObject AS (
    MyObjectId INT IDENTITY NOT NULL PRIMARY KEY,
    MyObjectField1 VARCHAR(100) NOT NULL,
    MyObjectField2 VARCHAR(100) NOT NULL,
    MyObjectField3 VARCHAR(100) NOT NULL,
)
CREATE TABLE MyObjectHistory AS (
    MyObjectHistoryId INT IDENTITY NOT NULL PRIMARY KEY,
    MyObjectId INT NOT NULL FOREIGN KEY REFERENCES MyObject(MyObjectId),
    MyObjectTrackedField1 VARCHAR(100) NOT NULL,
    MyObjectTrackedField2 VARCHAR(100) NOT NULL,
    MyObjectTrackedField3 VARCHAR(100) NOT NULL,
)
cubetwo1729
quelle
Ich bin mit @Joel
HaBo

Antworten:

7

Aus praktischen Gründen für den Datenzugriff sollten Sie die Struktur aus Ihrer ersten Option verwenden, aber stattdessen alle Versionen Ihrer verfolgten Spaltenwerte einschließlich der aktuellen Version in Ihrer Verlaufstabelle behalten .

Der Grund dafür ist, dass Sie im Allgemeinen, wenn Sie sich die Geschichte ansehen möchten, die aktuelle und alle früheren Versionen einbeziehen möchten. Wenn Sie die Geschichte nicht betrachten möchten, möchten Sie sie aus dem Weg räumen. In vielen Fällen bedeutet dies, den Verlauf in ein separates Schema oder eine separate Datenbank zu trennen. Selbst wenn Sie Ihren Verlauf im selben Schema wie Ihre aktuellen Daten belassen, sind alle Abfragen, die historische Daten (einschließlich der aktuellen Werte) anzeigen, viel komplexer, da sie im Wesentlichen zwei Quellen zusammenführen müssen.

Joel Brown
quelle
2

Ich würde die erste Version bevorzugen, da Sie den Verlauf wahrscheinlich nur selten sehen müssen, aber häufig den aktuellen Wert sehen müssen. Eine Verlaufstabelle sollte über einen Trigger ausgefüllt werden, damit Sie sich keine Sorgen machen müssen, dass die Daten im Allgemeinen nicht mehr synchron sind. Angenommen, Sie haben eine Million Datensätze in MyObject und dann 10.000.000 Datensätze in MyObjectHistory. Möchten Sie wirklich einer Tabelle mit so vielen Datensätzen beitreten, um den aktuellen Wert zu erhalten?

Wenn Sie nun den Verlauf häufiger oder häufiger als den aktuellen Wert abfragen müssen, funktioniert die zweite Struktur. (Und wenn Sie den Wert ab einem bestimmten Datum anzeigen möchten, würde ich ein Feld für Anfang und Ende des Datums haben, um die Abfrage zu vereinfachen.)

Übrigens würde ich der Verlaufstabelle ein Datumsfeld hinzufügen, um feststellen zu können, in welcher Reihenfolge die Änderungen vorgenommen wurden. Sie können sich nicht auf Identitäten für die zeitliche Reihenfolge verlassen. PLus, wenn es eine Frage zu einem vorherigen Wert gibt und wenn er sich ändert, müssen Sie wissen. Ich könnte auch Werte für die Anwendung eingeben, von der die Änderung stammt (wenn Sie mehrere Anwendungen haben) und / oder für die Person, die die Änderung vorgenommen hat.

HLGEM
quelle
0

Es gibt einige wichtige Gründe für # 1. Das erste ist das Größenproblem, auf das HLGEM hinweist, aber es gibt auch andere wichtige.

In der Regel werden sich in Ihrem Audit-Trail im Laufe der Zeit Anforderungen entwickeln. Möglicherweise möchten Sie Datenbankbenutzer, den Zeitpunkt der Änderung usw. verfolgen. Die Anforderungen an den Prüfpfad und Ihre Haupttabelle ändern sich wahrscheinlich im Laufe der Zeit etwas unabhängig voneinander. Schließlich möchten Sie wahrscheinlich Audit-Trail-Daten nach einer bestimmten Zeit unabhängig und in einer vollständig separaten Tabelle löschen.

Natürlich kann es Fälle geben, in denen Sie sie vollständig zusammenführen möchten (wie wir es für Steuersätze in LedgerSMB tun), da historische Daten für aktuelle Berechnungen verwendet werden können und die Anzahl der Datensätze wahrscheinlich relativ gering ist.

Ich werde jedoch vorschlagen, dass das Speichern von Objekten in solchen Tabellen selten zu guten, normalisierten Designs führt. Nach meiner Erfahrung möchten Sie wirklich eine Kapselung zwischen einem guten normalisierten Speicher und einem Anwendungsobjektmodell.

Chris Travers
quelle
2
Was meinen Sie mit "Kapselung zwischen gut normalisiertem Speicher und einem Anwendungsobjektmodell"? Würden Sie diese Idee erläutern oder ein Beispiel geben?
cubetwo1729