Skip to main content
13.09.2021: EIN EXPERTENBEITRAG

DATABASE SCHEMA CHANGES MIT DBT


Paul Batzdorf am 13.09.2021

Durch das Ersetzen eines Quellsystems ändert sich die Datengrundlage und die bisherige Analysepipeline funktioniert nicht mehr? Wie gehen wir damit um?

Im Umgang mit Data Warehouses (DWH) kommt es häufig vor, dass wir den Aufbau aus verschiedenen Gründen verändern müssen. Dabei geht es meist darum, zusätzliche Information einer Quelle in unsere Modelle einfließen zu lassen, tiefergehende Fragen mit unseren Daten beantworten zu können, veränderte Unternehmenslogik so zu übersetzen, dass sich dies auch im Datenfluss widerspiegelt, oder komplett neue Analysen/Modelle aufzubauen, die uns neue Einblicke verschaffen. Kürzlich sind wir bei einem unserer Kunden mit einer etwas selteneren Problemstellung konfrontiert worden: Schema Changes aufgrund von veränderten Quellsystemen.

Egal, welche Methode wir zum Modellieren unseres DWHs verwenden (Data Vault 2.0, Kimball, Inmon, etc.), orientieren wir uns beim Hinzufügen von Datenquellen immer auch am Ausgangsschema und den verfügbaren Informationen. Wie gehen wir also vor, wenn wir nun mit einem neuen Quellschema für die gleichen Daten auskommen müssen? Und wie können wir sichergehen, dass wir den Übergang zwischen den Systemen möglichst problemlos gestalten? Die verschiedenen Implementierungsmethoden haben hier unterschiedliche Vorgehensweisen und sind unterschiedlich gut dafür geeignet, mit solchen Änderungen umzugehen.

In diesem Fall wurde im DWH keine dieser Modellierungsmethoden strikt umgesetzt. Als DWH diente uns Snowflake. Wir verwenden dbt (data build tool) zur Transformation und das Quellsystem ist ein SaaS ERP System, das durch ein proprietäres ERP System ersetzt wird. Unser Vorgehen beim Laden der Tabellen ist: Wir laden die Quelle komplett und ohne vorherige Transformation ins DWH und fügen erst im Anschluss Transformationen durch. Dies gilt sowohl für das alte als auch das neue ERP System.

Zuallererst gilt es eine Bestandsaufnahme zu machen. In welchen Modellen (Modelle in dbt sind Repräsentationen von Objekten im DWH: Tables, Views, Materialized Views etc.) verwenden wir Informationen aus unserem Quellsystem? In unserem Fall: Ziemlich viele, daher konzentrieren wir uns im ersten Moment auf die Modelle, die direkt an unsere Quelltabellen angeschlossen sind. Dabei hilft uns die Docs Seite von dbt. Jedes dbt Projekt erstellt auf Wunsch eine Docs Seite, auf der wir Informationen zu unseren DWH Objekten finden. Für uns ist der DAG (Directed Acyclic Graph) besonders wertvoll und könnte so aussehen:

Alle weiteren Schritte sind abhängig davon, wie das DWH aufgebaut ist und inwiefern sich das Schema des zu ersetzenden Systems vom alten unterscheidet. Außerdem kommt es auch darauf an, ob wir alle alten Informationen des alten Systems weiterhin für Analysen behalten wollen, auch wenn diese nicht im neuen System vorhanden sind.

In unserem Beispiel haben wir uns dafür entschieden, nur die Informationen aus dem historischen System zu behalten, die aktuell im Warehouse verwendet werden. Außerdem haben wir es mit einem dynamisch gewachsenen DWH zu tun und dies ist eine gute Möglichkeit für uns, die Struktur des DWHs an dieser Stelle noch einmal zu überdenken.

Nachdem wir also die Modelle identifiziert haben, in denen wir unsere Quelldaten weiterverarbeiten, erstellen wir ein Model, das sich aus den Quelldaten speist und die relevanten Spalten enthält, die wir in den weiteren Modellen nutzen. Es ist sehr wahrscheinlich, dass es sinnvoll ist, die relevanten Spalten in mehreren Modellen je nach Zugehörigkeit zu gruppieren. In diesen Modellen beziehen wir auch Transformationen mit ein, die wir in späteren Schritten mehrmals vorgenommen haben und daher vielleicht an einer früheren Position des DAGs besser aufgehoben wären wie Währungsumrechnungen oder andere Businesslogik, die sich in nachgelagerten Modellen wiederholt. Falls sich diese Struktur schon im DWH befindet, entfällt dieser Schritt.

Als nächstes binden wir das neue System an unser DWH an und versuchen diese erstellten Tabellen mit Spalten aus dem neuen System nachzubauen. Hilfreich war in diesem Schritt, dass beide Systeme eine Zeit lang parallel gelaufen sind, somit konnten die einzelnen Inhalte der beiden Systeme miteinander verglichen werden. Dies war nicht nur hilfreich für die Implementierung im DWH, sondern auch zur Überprüfung des Systems an sich, da so der Output beider Systeme einfach miteinander verglichen werden konnte. Falls das neue Datenmodell nicht mit der alten Modellierung kompatibel ist, kann nun noch aufbauend auf den alten Daten die Modellierung entsprechend angepasst werden. Ziel sollte es sein, dass wir am Ende mit der neuen Quelle ein Replikat des historischen Schemas haben. Dabei müssen im historischen Schema mindestens alle Informationen vorhanden sein, die in weiteren Transformationsschritten benötigt werden. Alle diese Informationen sollten sich im neuen Schema spiegeln und in den gleichen Tabellen mit den gleichen Spaltenbezeichnungen zu finden sein. Im Schema für das neue System können wir nun auch noch weitere Felder hinzufügen, die uns der Systemwechsel eventuell zu Verfügung stellt. Dies kann allerdings auch noch in späteren Iterationsschritten geschehen. Jeweils zwei identische Tabellen aus dem Schema der beiden Systeme werden jetzt mittels der dbt_utils Funktion geunioned.

Select * from {{dbt_utils.union_relations(relations=[ ref(‘stg__order_[oldsystem]’), ref(‘stg__orders_[newsystem]’)}}

dbt utils ist ein dbt Package, in dem sich verschiedene Funktionen wiederfinden, die in vielen Fällen hilfreich sein können. Diese Funktionen sind in Form von Makros hinterlegt und reichen von datenbankübergreifenden Implementierungen einer Datediff Funktion bis hin zu Funktionen, die eine Datespine Spalte befüllen. Die Funktion Union Relations ist in diesem Fall besonders hilfreich, da die beiden Tabellen nicht die exakt gleichen Spalten enthalten müssen. Es werden nur die Spalten mit gleichem Namen vereinigt; die Spalten, die nur in einer Tabelle vorhanden sind, werden in den Zeilen der anderen Tabelle mit NULL-Werten aufgefüllt.

Dadurch gewinnen wir eine komplette Historie der benötigten Spalten über beide Systeme in einer Tabelle. Falls in Zukunft neue Informationen aus einem der Systeme benötigt werden, können diese noch in einem späteren Schritt hinzugefügt werden. Somit haben wir eine Lösung erreicht, mit der wir auch in Zukunft flexibel auf weitere Anpassungen reagieren können.