Über Links im Data Vault
Petr Beles über Data-Vault-Links für Transaktionen: Muster, Stolperfallen und Designentscheidungen bei der Modellierung von Transaktions-Links.
Petr Beles, 2150 GmbH, https://staging.datavault-builder.com 2017-02-11 Links representing transactions in Data Vault In diesem Artikel beschäftige ich mich ausschließlich mit Links, die Transaktionen repräsentieren. Ich lasse bewusst Links außer Acht, die Beziehungen darstellen (d. h. Stammdaten-Beziehungen = die einem Objekt Kontext verleihen, wie der Link zwischen einer Stadt und einer Gemeinde). Eine Zeile in einem konventionellen Link im Data Vault wird durch eine eindeutige Kombination von Beziehungen definiert (dargestellt durch den Hash der zusammengesetzten BKs aller beteiligten Hubs), ggf. ergänzt durch einen Dependent Child Key. (Building a scalable DWH with DV 2.0 - Linstedt & Olschimke 4.4.5)
Order UID Sales Order ID Order Line Number Customer Product Quantity Price Total
345789 SO-02155 1 Smith Bottle of Wine 6 20$ 120$
Alle Beziehungen würden zur Identifizierung dieser Zeile herangezogen, und alle Attribute würden in einen Link-Satellite geladen.
In der Realität haben Transaktionen häufig eindeutige Business Keys (die Nummer, die dem Kunden mitgeteilt wird – nicht der technische Schlüssel). Nehmen wir eine Bestellung als Beispiel: Eine Bestellung hat eine Bestellnummer, und jede Zeile dieser Bestellung hat eine Zeilennummer. Auch wenn die Zeilennummer für sich allein eine schwache Entität ist, lässt sie sich eindeutig identifizieren, wenn man die Transaktions-ID mit der Zeilennummer kombiniert. Selbst wenn die Zeilennummer nicht auf einem Bestellschein abgedruckt ist, ist sie vorhanden und kann von einem Kunden telefonisch referenziert werden.
Order UID Sales Order ID Order Line Number Customer Product Quantity Price Total
345789 SO-02155 1 Smith Bottle of Wine 6 20$ 120$
Identifizierende Teile, wie sie durch den Geschäftsprozess definiert sind.
Das bedeutet, dass der Hash-Key[1] von identifizierenden Beziehungen abhängt – wie jene zur Transaktion (sofern explizit modelliert) – und vom Dependent Child Key wie der Bestellzeilennummer. Das Problem ist, dass auch nicht-identifizierende Teile wie die Kunden- oder Produktbeziehung zur Bildung des Hash-Keys einer Zeile in der Link-Tabelle herangezogen werden. Mit anderen Worten: Der Hash-Key, der die Kombination aller Business Keys plus dem Dependent Child darstellt, ist de facto ein Hashdiff-Key und kein Transaktionsidentifikator.
Daran ist prinzipiell nichts falsch. Ein Hashdiff-Key kann ein sehr leistungsstarkes Werkzeug sein, um zu bestimmen, ob eine Zeile eingefügt werden muss, da man nur den Index dieser Spalte lesen muss, um zu entscheiden, ob eine neue Zeile in die Link-Tabelle eingefügt wird. Wenn sich die nicht-identifizierenden Beziehungen einer Transaktion nach der Übergabe an den Data Vault nicht mehr ändern, entspricht die Granularität des Links sogar der Granularität der Transaktionen.
Das Problem: In vielen Branchen können sich nicht-identifizierende Beziehungen einer Transaktion nach ihrer Übergabe an den Vault ändern oder erst danach eintreffen – wie z. B. ausgetauschte Kunden oder Produkte. In meiner Erfahrung aus der Fertigungsindustrie handelt es sich dabei um Standardgeschäftsprozesse.
Ein Beispiel, das ich gerne nenne, ist der Prozess, bei dem ein potenzieller Kunde anruft, um die Verfügbarkeit eines Produkts zu prüfen: Der Call-Center-Agent legt zunächst eine Bestellung für einen Dummy-Kunden an, da es sich nicht lohnt, ein neues Kundenkonto zu eröffnen, wenn das Produkt nicht vorrätig ist. Nachdem die Verfügbarkeit geprüft wurde, notiert er die Kontaktdaten auf Papier und gibt sie erst später ein, nachdem der Kunde aufgelegt hat – damit der Kunde nicht warten muss, falls das IT-System nicht reagiert. Bei hohem Anrufvolumen erfolgt die Dateneingabe manchmal erst am nächsten Tag, wenn die erste Version der Transaktion bereits in den Vault geladen wurde.
Ein weiteres Beispiel: Das Produkt einer Bestellung wird gegen ein neueres Produkt ausgetauscht, weil das bestellte Produkt nicht mehr verfügbar ist, das neue aber ein perfekter Ersatz ist.
Beide validen Geschäftsprozesse führen zu einem zweiten Eintrag im konventionellen Data-Vault-Link.
Verursacht das ein Problem für die Link-Tabellen, wie sie standardmäßig beschrieben werden? Aus technischer Sicht nicht: Für jede Änderung des Kunden oder Produkts wird ein neuer Hash-Key generiert und eine neue Zeile eingefügt. Es handelt sich jedoch nicht um eine SCD-Type-2-Tabelle, da jede Kombination aus Transaktion zu Kunde/Produkt nur einmal eingefügt wird. Wenn man die Gültigkeit der verschiedenen Versionen in einem Link-Satellite verfolgt, lassen sich alle möglichen Kombinationen eingehender Änderungen erfassen. Kurz gesagt: Ich halte das konventionelle Design für sehr gut auf schnelle Schreibgeschwindigkeiten ausgelegt, in der Lage, alle eingehenden Änderungen zu verfolgen, und es hat sich in der Praxis bewährt.
Warum also etwas ändern, das funktioniert?
Ehrlich gesagt, weil ich die Granularität der Link-Tabelle nie wirklich verstanden habe: Eine Transaktion hat beim ersten Eintreffen zunächst eine Zeile. Wenn sich der Kunde einer Bestellung ändert – wie oben beschrieben –, wird eine neue Zeile eingefügt. Wir haben nun zwei Zeilen in der Link-Tabelle für dieselbe Transaktion. Wenn die Bestellung auf den ersten Kunden zurückgesetzt wird, hat sie immer noch zwei Zeilen. Ich will nicht falsch verstanden werden: Alle Informationen sind vorhanden, wenn man einen Tracking-Satellite hat und weiß, wie man ihn abfragt. Aber auf logischer Ebene die Granularität der Tabelle einem Report-Designer zu erklären, ist – zumindest für mich – irgendwie kompliziert.
Ja, man kann und muss dieses Szenario auf einer Schnittstelle zwischen dem Vault und der Reporting-Schicht abstrahieren, aber man verlagert die Komplexität nur.
Das zweite und wichtigere Problem ist ein logisches: Link-zu-Link-Links müssen vermieden werden, da man nicht nur agile Prozesse aufgrund von Abhängigkeiten brechen würde, sondern auch Versionen der Transaktion miteinander verknüpfen würde, anstatt die Transaktion selbst.
Außerdem ist diese Lösung nicht optimal, wenn man Beziehungen hat, die verspätet eintreffen – wie Tickets, die Kunden erst später im Geschäftsprozess zugewiesen werden.
Welche Änderungen könnten die Situation meiner Meinung nach verbessern?
-
Einen Hub erstellen, der stets die feinste Granularität der Transaktion repräsentiert. Im obigen Beispiel: Sales Order Line mit einem BK aus Bestellnummer in Kombination mit der Zeilennummer[2]
-
Den Link zu diesem Hub in der Transaktion einschließen. Den Dependent Child Key im Link weglassen, da er nicht mehr benötigt wird
-
Alle Kontextattribute der Transaktion als normalen Satellite mit dem Hub verbinden, anstatt sie mit dem Link zu verbinden
-
Die Tracking-Satellites, die auf fehlende Keys in der Quelle prüfen, mit diesem Hub verbinden, anstatt mit dem Link
-
Entscheiden, ob sich die Transaktion nach ihrer Übergabe an den Data Vault noch ändern kann
Wenn ja: Den Link als SCD-Type-2-Load basierend auf diesem treibenden Hub laden – man kann eine virtuelle oder materialisierte SCD-Type-1-View auf diesem Link erstellen, indem man nach dem Keyed-Instance-Hub-Key partitioniert
-
Wenn nein: Den Link als SCD-Type-0-Load basierend auf diesem treibenden Hub laden (oder eine SCD-Type-0-View auf der SCD-Type-2-Tabelle erstellen, wenn man der Quelle nicht vertraut – virtuell oder materialisiert)
-
Wenn Performance ein Problem ist, kann man dennoch den Hashdiff-Ansatz verwenden, um die SCD-Type-2-Link-Tabelle zu laden (für SCD Type 0 reicht der Vergleich nur auf dem Hash des BK des treibenden Hubs)
-
Wenn man zwei Transaktionen verknüpfen möchte, einfach einen Link zwischen den beiden Keyed-Instance-Hubs auf der richtigen Granularität dieser Transaktionen erstellen
Hub Sales Order Line
Sales Order ID Order Line Number Sales Order Line Hash
SO-02155 1 85036a318c424cdd…
Link Sales Order Line
Load Time Sales Order Line Hash Product Hash Customer Hash
02.11.2012 11:22 85036a318c424cdd… 2bdf8a1484a74e1d… 1e292c434a272b21…
Sat Sales Order Line
Load Time Sales Order Line Hash Quantity Price Total
02.11.2012 11:22 85036a318c424cdd… 6 20$ 120$
Technische Darstellung von Hub, Links und Satellite. Identifizierende Teile von Zeilen gelb markiert. Einige technische Felder, Tracking-Satellite und Sales-Order-Hub aus Lesbarkeitsgründen weggelassen.
Interessanterweise tut man technisch gesehen beim Laden genau dasselbe wie beim Laden eines Satellites: Man vergleicht Attribute (verknüpfte Hash-Keys) basierend auf dem Hash-Key des Hubs.[3]
Zusätzlich wird das Hinzufügen nicht-identifizierender Link-Teile absolut agil, da sie einfach mit demselben Hub verbunden werden und verlustlos mit dem bestehenden Link vereinigt werden können.
Und als letzten Punkt: Man kann optionale Teile eines Links in eine separate, dünn besetzte Tabelle auslagern. Mit dem Hub, der die richtige Granularität der Transaktion hält, lassen sich obligatorische und optionale Teile zusammenführen. Man wende einfach dasselbe Muster an wie beim Kombinieren verschiedener Satellites.
Link Sales Order Line Store
Load Time Sales Order Line Hash Store
04.11.2012 11:22 85036a318c424cdd… 416360b329ad4ddab39dde7ee5c97479
Zusätzlicher Link, separat modelliert, um den entweder später implementierten, optionalen oder verspätet eintreffenden Link-Teil zu erfassen
Widerspricht das dem Data-Vault-Standard?
Ehrlich gesagt glaube ich für die meisten Teile nicht:
-
Nichts sollte daran hindern, einen Hub auf der feinsten Granularität einer Transaktion zu erstellen und ihn als „Keyed-Instance"-Hub zu bezeichnen
-
Attribute abhängig von einem Hub-Key als Satellite statt als Link-Satellite speichern? Ich sehe dabei kein Problem.
-
Ist es möglich, Links zu implementieren, die wirklich durch alle verknüpften Objekte identifiziert werden? Ja, das ist möglich, indem alle Schlüssel im Hub kombiniert werden.
-
Kann alles, was geliefert wird, aus den gespeicherten Daten reproduziert werden? Ja
-
Ist Verbundtreue (Losslessness) gewährleistet? Ja
-
Gibt es Unterschiede für Non-Historized-Links? Nein
Es gibt jedoch einen Vorbehalt: Die Implementierung des Links als SCD-Type-2-Load basierend auf dem Keyed-Instance-Hub verändert die Anzahl der Zeilen in der Link-Tabelle.
In meinem Beispiel mit einer Bestellung, die von Kunde A zu B und zurück wechselt, hätte man beim ursprünglichen Muster nur 2 Zeilen:
Data Vault Standard Link
Load Time Sales Order ID Order Line Number Customer Product
02.11.2012 11:22 SO-02155 1 Smith Bottle of Wine
04.11.2012 11:22 SO-02155 1 Miller Bottle of Wine
Logische Darstellung. Technisch würden Customer und Product als Hashes gespeichert, und ein Link-Hash würde basierend auf den orangen Feldern hinzugefügt. 3 Zeilen würden im Tracking-Link-Satellite gefunden.
Im neuen Design hat man nun 3 Zeilen. Der PK ist nicht der Hashdiff-Key basierend auf allen Teilen, sondern der Keyed-Instance-Hub-Hash-Key + Load Time.
Link Sales Order Line
Load Time Sales Order Line Product Customer
02.11.2012 11:22 SO-02155 & 1 Bottle of Wine Smith
04.11.2012 11:22 SO-02155 & 1 Bottle of Wine Miller
06.11.2012 11:22 SO-02155 & 1 Bottle of Wine Smith
Logische Darstellung
Ja, es gibt also eine kleine Abweichung vom Standard, die meiner Meinung nach sinnvoll ist, da sie das Abfragen der Tabelle deutlich vereinfacht: Um die letzte Version der Transaktion zu erhalten, partitioniert man einfach nach dem treibenden Hub-Key und nimmt das neueste Ladedatum. Kein Join weiterer Tabellen ist nötig, um die neueste Version zu finden – was beim ursprünglichen Design erforderlich wäre. Gleiches gilt für die Erstellung einer vollständigen Historie der Transaktion: einfach nach dem treibenden Hub-Key partitionieren. Und am wichtigsten: Die Granularität ist wieder definiert – jede Version der Transaktion über die Zeit erhält ihre eigene Zeile.
Soll ich es verwenden?
Das liegt bei Ihnen. Ich bin jedoch zuversichtlich, dass das Muster wie beschrieben funktioniert – wir haben es in realen Szenarien erprobt, einschließlich einiger Echtzeit-Verarbeitungsfälle, und ich schätze die damit verbundenen Vorteile. Wenn Sie das ursprüngliche Muster bevorzugen, den Hash-Key verstehen und sicher sind, dass Sie nicht auf die beschriebenen Probleme stoßen werden – warum nicht?
Nutze ich es für alle Links? Nein! Aber ich nutze es für alle Links, die Transaktionen repräsentieren. Links zwischen Stammdaten, die Kontext darstellen, sind eine andere Geschichte, über die ich separat diskutieren werde.
[1] Wenn ich Hash-Key schreibe, meine ich Hash oder eine andere Form von Surrogatschlüssel. Nur für DV2.0 sind Hash-Keys verpflichtend. [2] Ich habe von der Genesee Academy gelernt, dass sie diesen Hub als „Keyed Instance" bezeichnen. [3] Da es technisch sehr ähnlich wie normale Satellite-Loads funktioniert, kann man dem Link auch normale Kontextattribute hinzufügen und einen Non-Historized-Link erstellen, der neben den Hash-Keys auch einige Kontextspalten enthält, ohne dabei technisch etwas an den Ladeverfahren zu ändern. Bitte beachten Sie, dass dies kein allgemein empfohlenes Design ist und nur in Hochvolumen-/Near-Real-Time-Szenarien eingesetzt werden sollte.