Im Rahmen der Modellierung von Data-Warehouse-Systemen haben sich in der Vergangenheit zwei Modelle etabliert: 3. Normalform (3NF) oder das Sternschema. Beide Ansätze haben Nachteile, die durch eine Modellierung nach Data-Vault beseitigt werden. Das Sternschema z. B. nimmt für die gesteigerte Performance eine denormalisierte Speicherung der Daten in Kauf. Durch diese Denormalisierung können schnell Probleme mit der Datenintegrität auftreten. 3NF-Modelle dagegen leiden in der Regel unter einer schlechten Lesegeschwindigkeit, da die Daten zunächst per JOIN zusammengesucht werden müssen. Beide Probleme können mittels Data-Vaults gelöst werden.

Basisbausteine

Im Rahmen der Modellierung von Data-Vaults existieren 3 Basisbausteine, mit denen (fast) das komplette Model abgebildet werden kann. Diese lauten wie folgt:

Hubs

Mittels sog. Hubs werden Business-Entitäten abgebildet. Die Tabellenstruktur für einen Hub muss daher zwingend einen Business-Schlüssel beinhalten, der diese Entität eindeutig identifiziert. Im einfachsten Fall könnte dies wie folgt aussehen:

+----------------+
| <business_id>  |
+----------------+
| business value |
| ...            |
+----------------+

Hubs sollten nach der Business-Entität benannt werden, die sie abbilden. Es ist nicht zwingend erforderlich, dass Hubs mit einem bestimmten Präfix anfangen. Sofern ein Team Konventionen für die Benennung von Hubs einführen möchte, hat sich in der Praxis wahlweise „H_” oder „HUB_” etabliert, wie z. B. „HUB_PERSON”. Sowohl bei Hubs als auch bei allen folgenden Tabellen ist es optional möglich einen technischen Schlüssel hinzuzufügen und diesen ebenfalls als PRIMARY KEY zu markieren.

Mittels sog. Links werden Hubs miteinander verbunden. Die Tabellenstruktur für einen Link muss daher immer mindestens zwei Identifier beinhalten - einen für jede Verbindung. Alle Identifier sollten als FOREIGN KEY den jeweiligen Hub referenzieren. Im einfachsten Fall könnte dies wie folgt aussehen:

+----------------+-------------+
| <business_id>  | <other_id>  |
+----------------+-------------+
| business value | other value |
| ...            | ...         |
+----------------+-------------+

Links sollten nach der Business-Relation benannt werden. Zum Beispiel könnte ein Hub für Personen (HUB_PERSON) und ein Hub für Firmen (HUB_COMPANY) mehrmals miteinander verbunden sein, um auszudrücken, dass eine Person sowohl Kunde bei einer Firma sein kann, als auch als Angestellter. Beide Verbindungen werden als separate Links im Data-Vault abgebildet. Sofern eine Konvention eingehalten werden soll/muss, hat sich in der Praxis „L_” oder „LINK_” etabliert, wie z. B. „LINK_EMPLOYMENT” oder „LINK_CUSTOMER”.

Satellites

Weder Hubs noch Links beinhalten Business-Daten. Für diese Aufgabe existieren Satellites, die sowohl mit Hubs als auch Links verbunden werden können. Neben der Referenz auf entweder Hub oder Link können in einem Satellite beliebig weitere Spalten hinzugefügt werden, um beliebige Daten zu erfassen. Ein einfacher Satellite könnte wie folgt aussehen:

+----------------+-------------+
| <business_id>  | <some_data> |
+----------------+-------------+
| business value | entry       |
| ...            | ...         |
+----------------+-------------+

Jeder Hub oder Link kann mehrere Satellites haben, daher empfiehlt es sich, Satellites nicht zu groß werden zu lassen, um den Überblick nicht zu verlieren. In der Praxis hat sich „S_” oder „SAT_” als Namenskonvention etabliert, wie z.B. „SAT_PERSON”.

Praktisches Beispiel

Im folgenden Beispiel werden wir nach und nach ein Data-Vault-Schema aufbauen und dazu Postgres als Datenbank benutzen. Alle Tabellen werden mit einem technischen Schlüssel modelliert. Als ersten Schritt werden die Hubs erstellt:

CREATE TABLE hub_person (
    hub_person_id SERIAL PRIMARY KEY,
    id_number     TEXT
);

Die Spalte hub_person_id wird per SERIAL-Type automatisch mit einer fortlaufenden Nummer befüllt und entspricht dem technischen Schlüssel, der weiter oben besprochen wurde. Die Spalte id_number beinhaltet die Ausweisnummer einer Person und ist in unserem Beispiel die Business-Sicht auf eine Person.

CREATE TABLE hub_company (
    hub_company_id  SERIAL PRIMARY KEY,
    tax_number      TEXT
);

Wie man am Hub für Firmen (HUB_COMPANY) gut sehen kann, sehen Hubs fast immer gleich aus. Es ändern sich in der Regel immer die Namen der Spalten, aber seltener die Struktur. Die Spalte hub_company_id dient wieder als technischer Schlüssel und tax_number ist der Business-Schlüssel.

Als nächstes erstellen wir zwei Relationen zwischen diesen Hubs:

CREATE TABLE link_employment (
  link_employment_id  SERIAL PRIMARY KEY,
  hub_person_id       BIGINT REFERENCES hub_person(hub_person_id),
  hub_company_id      BIGINT REFERENCES hub_company(hub_company_id)
);

CREATE TABLE link_customer (
  link_customer_id  SERIAL PRIMARY KEY,
  hub_person_id     BIGINT REFERENCES hub_person(hub_person_id),
  hub_company_id    BIGINT REFERENCES hub_company(hub_company_id)
);

Die Referenzen in diesen Links verweisen auf die technischen Schlüssel. Falls für einen Link kein technischer Schlüssel erzeugt werden soll, kann auch ein kombinierter PRIMARY KEY verwendet werden. Bei allen Referenzen auf diesen Link müssen dann allerdings auch immer zwei (oder mehrere) Spalten referenziert werden.

-- Beispiel mit kombinierten Primary Key
CREATE TABLE link_customer (
  hub_person_id     BIGINT REFERENCES hub_person(hub_person_id),
  hub_company_id    BIGINT REFERENCES hub_company(hub_company_id),
  CONSTRAINT link_customer_pk PRIMARY KEY (hub_person_id, hub_company_id)
);

Zum Abschluss legen wir noch Satellites an, um Daten zu diesen Hubs und Links abzuspeichern:

CREATE TABLE sat_employment_contract (
  sat_employment_contract_id  SERIAL PRIMARY KEY,
  link_employment_id          BIGINT REFERENCES link_employment(link_employment_id),
  entry_date                  DATE,
  exit_date                   DATE
);

CREATE TABLE sat_person_name (
  sat_person_name_id  SERIAL PRIMARY KEY,
  hub_person_id       BIGINT REFERENCES hub_person(hub_person_id),
  first_name          TEXT,
  last_name           TEXT
);

CREATE TABLE sat_company (
  sat_company_id SERIAL PRIMARY KEY,
  hub_company_id BIGINT REFERENCES hub_company(hub_company_id),
  name           TEXT,
  stock          MONEY
);

Es mag merkwürdig erscheinen, dass mehrere Einträge pro z. B. hub_company_id in sat_company erstellt werden können, aber in einem Data-Vault können und sollen alle Daten zu allen Zeiten abgelegt werden. Der technische Schlüssel kann als fortlaufende Versionsnummer interpretiert werden und sorgt dafür, dass bei Änderung an den Daten kein UPDATE, sondern ein INSERT ausgeführt wird. Beim Auslesen der Daten muss dann derjenige Eintrag gewählt werden, dessen Referenz auf den jeweiligen Hub übereinstimmt und deren technischer Schlüssel den höchsten Wert hat. Ein einfaches SELECT-Statement könnte wie folgt aussehen:

SELECT  hc.tax_number, sc.name, sc.stock
FROM    hub_company hc
          INNER JOIN sat_company sc
            USING (hub_company_id)
ORDER BY sc.sat_company_id DESC
LIMIT 1

Sofern diese Form der Historisierung/Versionierung nicht erwünscht ist, kann natürlich weiterhin auf UPDATE-Statements zurückgegriffen werden. In diesem Fall sollte die Referenz zwischen Satellite und Hub/Link als UNIQUE markiert werden, um sicherzuerstellen, dass keine unnötigen Einträge erstellt werden.

Die Einträge in Hubs, Links und Satellites müssen in eben dieser Reihenfolge geschrieben werden, um sicherzustellen, dass die referentielle Integrität gewährleistet ist. Also zunächst neue Einträge in den Hubs erstellen, dann die Relationen mittels der Links aufbauen und abschließend die Satellites mit Daten befüllen.

Fortgeschrittene Themen

Referenztabellen

Um eindeutige Werte, wie z. B. der Name einer Währung, nicht mehrfach in Satellites zu speichern, können Referenztabellen verwendet werden. Diese sind ähnlich aufgebaut wie Satellites, da aber in der Regel kein zugehöriger Hub existiert, entfällt die Referenz auf diesen. Eine Referenztabelle für Währungen könnte z. B. wie folgt aussehen:

+-------------+-------------+
| currency_id | name        |
+-------------+-------------+
| 123         | EUR         |
| ...         | ...         |
+-------------+-------------+

Alle Satellites sollten dann nicht mehr den Wert EUR beinhalten, sondern nur noch eine Referenz auf die Währung mit der currency_id 123, über die dann der ursprüngliche Wert ermittelt werden kann. Die Referenz von Satellite zu Referenztabelle sollte als FOREIGN KEY abgebildet werden, um sicherzustellen, dass keine unbekannten Werte gespeichert werden.

Hierarchien

Um Hierarchien in Data-Vault abzubilden ist mindestens ein Hub und ein Link notwendig. Um z. B. eine Hierarchie unter Mitarbeitern im HUB_EMPLOYEE auszudrücken, wird ein Link LINK_EMPLOYEE_HIERARCHY erstellt, der zwei mal HUB_EMPLOYEE referenziert.

CREATE TABLE link_employee_hierarchy (
  boss      BIGINT REFERENCES hub_employee(hub_employee_id),
  employee  BIGINT REFERENCES hub_employee(hub_employee_id),
  CONSTRAINT link_employee_hierarchy_pk PRIMARY KEY (parent, child)
);

Audit

In den bisher gezeigten Tabellenstrukturen fehlt bislang Informationen, woher die Daten stammen, wann sie geschrieben und insbesondere auch von wem bzw. von welchem Prozess. Diese Art von Auditinformationen mag für eine einzelne Anwendung ein Overkill sein, für ein Data-Warehouse sind diese Informationen aber essentiell. Denkbar wäre z. B. eine weitere Spalte pro Hub/Link/Satellite, die auf eine Audit-Tabelle verweist. In dieser Tabelle befinden sich dann Informationen für jeden einzelnen Ladevorgang.

Fazit

Die Modellierung nach Data-Vault ist flexibel, da zukünftige Änderung vorgenommen werden können, ohne dass bestehende Strukturen geändert werden müssen. Sollen neue Business-Entitäten hinzugefügt werden, wird ein neuer Hub angelegt. Wurde eine neue Relation zwischen zwei Hubs identifiziert, wird ein neuer Link angelegt und sofern neue Daten erfasst werden sollen, wird ein neuer Satellite angelegt.

Gleichzeitig bietet Data-Vault eine sehr hohe Schreibperformance, da parallel in alle Hubs, dann parallel in alle Links und abschließend parallel in alle Satellites geschrieben werden kann. Data-Vaults skalieren daher beliebig mit der vorhandenen Hardware bzw. den vorhandenen Datenvolumen.

Die Lesegeschwindigkeit ist allerdings ohne weitere Arbeiten schlecht. Wie am obigen SELECT-Beispiel zu sehen ist, muss für eine Abfrage in der Regel mindestens ein JOIN ausgeführt werden. Durch eine Voraggregation der Daten, z. B. mittels einer MATERIALIZED VIEW oder einem gesonderten Data-Mart, bringt die Performance aber wieder auf ein Maß, dass sich mit dem Sternschema messen kann.

Im Ergebnis haben wir so ein Datenbankschema, das wir beliebig um weitere Tabellen/Spalten erweitern können, sehr schnell Schreiben und mit wenig Aufwand auch sehr schnell wieder Lesen können.

Weiteres zum Thema finden sie im Buch von Daniel Linstedt.