Positionierung
ETL-Prozesse sind das Rückgrat jeder Datenplattform. Sie entscheiden darüber, ob Berichte, Kennzahlen und Dashboards verlässlich, vollständig und rechtzeitig zur Verfügung stehen. Ein Data Warehouse ist immer nur so gut wie die Ladeprozesse, die es befüllen. Genau hier liegt mein Schwerpunkt: Ich entwickle ETL- und ELT-Strecken, die nicht nur im ersten Lauf funktionieren, sondern auch im Dauerbetrieb stabil, nachvollziehbar und wartbar bleiben.
Seit 1994 arbeite ich mit Datenbanken, seit Anfang der 2000er-Jahre mit Data-Warehouse- und ETL-Technologien. In dieser Zeit habe ich ETL-Prozesse mit sehr unterschiedlichen Werkzeugen umgesetzt: mit Microsoft SSIS und T-SQL, mit Informatica PowerCenter, mit Oracle Data Integrator, mit Teradata-BTEQ-Skripten, mit Shell-, Perl- und PowerShell-Skripten sowie zunehmend mit Azure Data Factory, Azure Synapse und Databricks. Diese Bandbreite erlaubt es mir, Ladeprozesse nicht aus der Sicht eines einzelnen Tools zu betrachten, sondern aus der Sicht der eigentlichen Aufgabe: Daten zuverlässig von A nach B zu bringen und dabei fachlich korrekt zu transformieren.
Diese lange Erfahrung über viele Technologiewechsel hinweg prägt meine Arbeitsweise. Ich habe erlebt, wie ETL-Landschaften von rein hostbasierten Strecken über client-server-orientierte Werkzeuge bis zu cloudnativen Pipelines gewandert sind. Was sich dabei nie geändert hat, sind die eigentlichen fachlichen Fragen: Sind die Daten vollständig? Sind sie korrekt? Sind sie rechtzeitig da? Lässt sich nachvollziehen, woher ein Wert stammt? Genau auf diese Fragen richte ich jede ETL-Strecke aus, unabhängig davon, ob sie mit SSIS, mit T-SQL oder mit Databricks umgesetzt wird. Werkzeuge sind Mittel zum Zweck – die fachliche Zuverlässigkeit ist das Ziel.
Auftraggeber holen mich typischerweise dann ins Boot, wenn es um belastbare Datenstrecken geht, die im Tagesgeschäft tragen müssen: Berichte für das Controlling, regulatorische Auswertungen im Banken- und Behördenumfeld, Kennzahlen für das Management oder Datenlieferungen an nachgelagerte Systeme. In all diesen Fällen ist ein Ausfall der ETL-Strecke kein technisches Randproblem, sondern hat unmittelbare fachliche Folgen. Diese Verantwortung nehme ich ernst und plane Strecken deshalb von Beginn an auf Stabilität, Nachvollziehbarkeit und Wartbarkeit hin.
Was ETL-Entwicklung in der Praxis bedeutet
ETL steht für Extract, Transform, Load. In der Praxis ist das jedoch nur die grobe Klammer um eine ganze Reihe von Detailaufgaben, die über Erfolg oder Misserfolg eines Datenprojekts entscheiden. Wer ETL-Prozesse nur als „Daten von der Quelle in die Zieltabelle kopieren“ versteht, wird früher oder später von der Realität eingeholt: von wechselnden Quellsystemen, von Datenqualitätsproblemen, von Lastspitzen, von Quellen, die mitten im Lauf nicht erreichbar sind, und von fachlichen Anforderungen, die sich im Laufe der Zeit ändern.
Extract – das Lesen der Quellen
Der Extrakt-Schritt liest Daten aus den Quellsystemen. Das klingt einfach, ist aber der Schritt, der am meisten Fingerspitzengefühl verlangt. Quellsysteme sind selten dafür gebaut, große Datenmengen für ein Data Warehouse zu liefern. Sie sind operative Systeme – ERP, CRM, Webshops, Fachverfahren – die im laufenden Betrieb nicht durch riesige Abfragen ausgebremst werden dürfen. Deshalb arbeite ich beim Extrakt mit klar definierten Lesefenstern, inkrementellen Verfahren, Change Data Capture und – wo nötig – mit dateibasierten Schnittstellen über sFTP.
In meinen Projekten habe ich Quellen sehr unterschiedlicher Art angebunden: relationale Datenbanken wie SQL Server, Oracle, Informix und Teradata, NoSQL-Quellen wie MongoDB, Flat-Files im Fixed-Length- und CSV-Format, XML-Dateien, REST- und OData-APIs sowie Host-Datenfiles, deren Struktur über COBOL- und PL/1-Copybooks beschrieben war. Gerade die Anbindung von Altsystemen zeigt, dass der Extrakt mehr ist als ein SELECT: Es geht um das Verstehen der Quellstruktur, der Datentypen, der Sonderfälle und der fachlichen Bedeutung.
Transform – die fachliche Logik
Die Transformation ist das Herzstück. Hier werden Daten bereinigt, vereinheitlicht, zusammengeführt, angereichert, historisiert und an die Zielstrukturen angepasst. Typische Aufgaben sind die Vereinheitlichung von Schlüsseln, das Auflösen von Code-Tabellen, die Deduplizierung, die Behandlung von NULL-Werten, das Mapping fachlicher Ausprägungen, die Berechnung abgeleiteter Kennzahlen und die Umsetzung von Geschäftsregeln. Entscheidend ist, dass jede Transformation nachvollziehbar bleibt: Welche Regel wurde angewendet, woher stammt ein Wert, warum sieht ein Datensatz so aus, wie er aussieht?
Load – das kontrollierte Schreiben
Der Lade-Schritt schreibt die transformierten Daten in die Zielstrukturen. Auch hier steckt der Teufel im Detail: Wird voll oder inkrementell geladen? Werden Historien gepflegt? Was passiert mit Datensätzen, die in der Quelle gelöscht wurden? Wie wird sichergestellt, dass ein abgebrochener Ladelauf nicht zu doppelten oder unvollständigen Daten führt? Diese Fragen beantworte ich nicht erst im Fehlerfall, sondern bereits beim Entwurf der Strecke.
- Inkrementelle Beladung statt teurer Full Loads, wo immer es die Quelle erlaubt
- Idempotente Ladeprozesse, die bei Wiederholung dasselbe Ergebnis liefern
- Klare Trennung von Staging, Core und Auslieferungsschicht
- Durchgängiges Logging mit Batch-IDs und Lauf-Metadaten
- Fachliche und technische Tests als fester Bestandteil der Entwicklung
Referenzarchitektur einer ETL-Strecke
Über viele Projekte hinweg hat sich eine Architektur bewährt, die Daten in klar getrennten Schichten verarbeitet. Jede Schicht hat eine definierte Aufgabe und einen definierten Verantwortungsbereich. Das macht die Strecke verständlich, testbar und erweiterbar.
Referenzarchitektur einer ETL-Strecke: Quellen, Extract, Staging, Transform, Core/DWH und BI-Auslieferung – flankiert von Orchestrierung, Logging und Wiederanlauf.
Die Quellen werden zunächst möglichst unverändert in eine Staging-Schicht geladen. Diese Rohdatenschicht dient als Audit- und Wiederanlaufpunkt: Sie hält fest, was tatsächlich geliefert wurde, und erlaubt es, einen Lauf zu wiederholen, ohne die Quellen erneut zu belasten. Erst in der Transformationsschicht werden die fachlichen Regeln angewendet. Das Ergebnis landet im Core beziehungsweise im Data Warehouse, das historisiert und für die Auswertung optimiert ist. Aus dem Core werden schließlich die Auslieferungsschichten für Berichte, Cubes und BI-Modelle befüllt.
Die Staging-Schicht erfüllt dabei mehrere Aufgaben gleichzeitig. Sie entkoppelt das Lesen der Quellen vom Verarbeiten der Daten, sodass kurze Lesefenster in den Quellsystemen genügen und die eigentliche, oft aufwändige Transformation unabhängig davon laufen kann. Sie dient als Beweis darüber, was tatsächlich geliefert wurde – gerade in regulierten Umfeldern ein wichtiger Aspekt. Und sie ermöglicht den Wiederanlauf: Bricht ein Lauf in der Transformation ab, lässt er sich aus dem Staging heraus wiederholen, ohne die Quellen ein zweites Mal zu belasten. In Projekten mit empfindlichen operativen Systemen ist genau diese Entkopplung oft der entscheidende Faktor für einen reibungslosen Betrieb.
Zwischen Core und Auslieferungsschicht ziehe ich bewusst eine Grenze. Der Core ist auf fachliche Korrektheit und Historisierung optimiert und folgt einer stabilen, langfristig gedachten Modellierung. Die Auslieferungsschicht hingegen ist auf die konkreten Auswertungen zugeschnitten – etwa als Star-Schema für ein Power-BI-Modell oder als denormalisierte Sicht für einen bestimmten Bericht. Diese Trennung erlaubt es, neue Auswertungen zu ergänzen, ohne den Kern des Data Warehouse anzufassen, und schützt umgekehrt bestehende Berichte vor Änderungen an der internen Modellierung.
Lademuster: Full Load, Delta und SCD2
Die Wahl des richtigen Lademusters entscheidet maßgeblich über Performance, Stabilität und Aussagekraft der Daten. In meinen Projekten kommen vor allem drei Muster zum Einsatz, oft kombiniert je nach Tabelle und Quellfähigkeit.
Drei Lademuster im Vergleich: Full Load, Delta/Inkrementell und Slowly Changing Dimension Typ 2.
Full Load
Beim Full Load wird die Zieltabelle vollständig neu aufgebaut – typischerweise per Truncate und anschließendem Insert. Das ist einfach und robust, eignet sich aber nur für überschaubare Datenmengen oder kleine Dimensionstabellen. Bei großen Faktentabellen wird der Full Load schnell zum Flaschenhals.
Delta- und inkrementelle Beladung
Bei der inkrementellen Beladung werden nur die seit dem letzten Lauf geänderten Datensätze verarbeitet. Dafür braucht es ein verlässliches Kriterium: einen Zeitstempel, eine fortlaufende ID, ein Change-Data-Capture-Verfahren oder einen Vergleich über Hash-Werte. Das folgende Beispiel zeigt eine typische, watermark-basierte inkrementelle Beladung in T-SQL.
-- Inkrementelle Beladung einer Staging-Tabelle anhand eines Hochwasserstands
-- (Watermark). Es werden nur Datensaetze gelesen, die neuer sind als der
-- letzte erfolgreiche Ladelauf.
DECLARE @LastWatermark datetime2(3);
DECLARE @NewWatermark datetime2(3);
-- 1) Letzten Stand aus der Steuerungstabelle holen
SELECT @LastWatermark = LastLoadedAt
FROM etl.LoadControl
WHERE TableName = N'SalesOrder';
-- 2) Aktuellen Hoechststand der Quelle ermitteln
SELECT @NewWatermark = MAX(ModifiedDate)
FROM src.SalesOrder;
-- 3) Nur die Delta-Menge in das Staging laden
INSERT INTO stg.SalesOrder (OrderId, CustomerId, Amount, ModifiedDate, BatchId)
SELECT s.OrderId, s.CustomerId, s.Amount, s.ModifiedDate, @@SPID AS BatchId
FROM src.SalesOrder AS s
WHERE s.ModifiedDate > @LastWatermark
AND s.ModifiedDate <= @NewWatermark;
-- 4) Steuerungstabelle erst nach Erfolg fortschreiben
UPDATE etl.LoadControl
SET LastLoadedAt = @NewWatermark,
LastRunAt = SYSUTCDATETIME()
WHERE TableName = N'SalesOrder';Der Watermark wird bewusst erst nach erfolgreichem Laden fortgeschrieben. Bricht der Lauf vorher ab, wird beim nächsten Mal dieselbe Delta-Menge erneut verarbeitet – der Prozess ist damit wiederanlauffähig.
Slowly Changing Dimensions Typ 2
Wenn nicht nur der aktuelle Stand, sondern auch die Historie eines Datensatzes wichtig ist, kommt das SCD-Typ-2-Muster zum Einsatz. Statt einen bestehenden Datensatz zu überschreiben, wird die alte Version mit einem Gültig-bis-Datum geschlossen und eine neue Version mit Gültig-von-Datum eingefügt. So lässt sich später jede Auswertung auf den damals gültigen Stand beziehen. Ich habe SCD2 sowohl klassisch in SSIS als auch in reinen T-SQL-Prozeduren über das MERGE-Statement implementiert.
-- Historisierung einer Dimension nach SCD Typ 2.
-- Geaenderte Datensaetze werden geschlossen und als neue Version eingefuegt.
MERGE dim.Customer AS tgt
USING stg.Customer AS src
ON tgt.CustomerBk = src.CustomerBk
AND tgt.IsCurrent = 1
WHEN MATCHED
AND (tgt.City <> src.City OR tgt.Segment <> src.Segment)
THEN UPDATE SET
tgt.ValidTo = SYSUTCDATETIME(),
tgt.IsCurrent = 0
WHEN NOT MATCHED BY TARGET
THEN INSERT (CustomerBk, City, Segment, ValidFrom, ValidTo, IsCurrent)
VALUES (src.CustomerBk, src.City, src.Segment, SYSUTCDATETIME(), NULL, 1)
OUTPUT $action, inserted.CustomerBk INTO etl.MergeLog;
-- Fuer geaenderte Datensaetze wird in einem zweiten Schritt die neue
-- aktuelle Version eingefuegt (klassisches SCD2-Zweischrittverfahren).
INSERT INTO dim.Customer (CustomerBk, City, Segment, ValidFrom, ValidTo, IsCurrent)
SELECT s.CustomerBk, s.City, s.Segment, SYSUTCDATETIME(), NULL, 1
FROM stg.Customer AS s
JOIN dim.Customer AS d
ON d.CustomerBk = s.CustomerBk AND d.IsCurrent = 0
AND d.ValidTo >= DATEADD(SECOND, -2, SYSUTCDATETIME());MERGE ist elegant, muss aber sorgfältig getestet werden. Für sehr große Tabellen setze ich aus Performance- und Sperrgründen häufig auf separate, mengenbasierte INSERT/UPDATE-Schritte.
Entwicklung mit SSIS und T-SQL
Microsoft SQL Server Integration Services (SSIS) ist seit vielen Jahren mein zentrales Werkzeug für ETL im Microsoft-Umfeld. Ich habe SSIS-Pakete in zahlreichen Projekten entwickelt, migriert, optimiert und stabilisiert – von der Stadtsparkasse München über Loyalty Partner (Payback) bis zur Bundesagentur für Arbeit. Dabei reicht die Bandbreite von einfachen Import-Strecken für Textdateien bis zu komplexen, parametrisierten Paketen mit Schleifen, Containern, Event-Handlern und dynamischer Konfiguration.
Ein wichtiger Grundsatz: SSIS ist stark beim Orchestrieren, beim Datenfluss und beim Anbinden heterogener Quellen. Die eigentliche mengenorientierte Transformation gehört aber oft in die Datenbank. Deshalb kombiniere ich SSIS bewusst mit T-SQL und Stored Procedures. Das Paket steuert den Ablauf, kümmert sich um Dateien, Logging und Fehlerbehandlung; die schweren Transformationen laufen als mengenbasierte SQL-Operationen direkt im SQL Server, wo sie am schnellsten sind.
SSIS bietet darüber hinaus eine Reihe von Bausteinen, die für robuste Strecken besonders wertvoll sind: Foreach-Loop-Container für die Verarbeitung ganzer Dateiverzeichnisse, Sequence-Container zur logischen Gruppierung, Event-Handler für eine zentrale Fehlerbehandlung sowie Variablen und Parameter für eine saubere, umgebungsabhängige Konfiguration. Ich nutze diese Mittel gezielt, um Pakete generisch und wiederverwendbar zu halten. Ein gut entworfenes Master-Paket kann so Dutzende Quelldateien einheitlich verarbeiten, ohne dass für jede Datei ein eigenes Paket gepflegt werden muss – das reduziert den Wartungsaufwand erheblich.
Strukturierte Stored Procedure als Ladebaustein
Damit ETL-Logik wartbar bleibt, kapsle ich Ladeschritte in klar strukturierte Prozeduren mit einheitlichem Aufbau: Parameterprüfung, Transaktionsklammer, Fehlerbehandlung über TRY/CATCH und ein durchgängiges Logging über eine zentrale Protokolltabelle. Das folgende Muster verwende ich in vielen Varianten.
CREATE OR ALTER PROCEDURE etl.LoadSalesFact
@BatchId bigint
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON; -- bei Fehler automatisch zuruecksetzen
DECLARE @Rows int = 0;
BEGIN TRY
EXEC etl.WriteLog @BatchId, N'LoadSalesFact', N'START';
BEGIN TRANSACTION;
-- Mengenbasierte Transformation direkt im SQL Server
INSERT INTO core.FactSales (DateKey, CustomerKey, ProductKey, Amount, BatchId)
SELECT d.DateKey, c.CustomerKey, p.ProductKey, s.Amount, @BatchId
FROM stg.SalesOrder AS s
JOIN core.DimDate AS d ON d.FullDate = CAST(s.ModifiedDate AS date)
JOIN core.DimCustomer AS c ON c.CustomerBk = s.CustomerId AND c.IsCurrent = 1
JOIN core.DimProduct AS p ON p.ProductBk = s.ProductId;
SET @Rows = @@ROWCOUNT;
COMMIT TRANSACTION;
EXEC etl.WriteLog @BatchId, N'LoadSalesFact', N'SUCCESS', @Rows;
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0 ROLLBACK TRANSACTION;
EXEC etl.WriteLog @BatchId, N'LoadSalesFact', N'ERROR', NULL, ERROR_MESSAGE();
THROW; -- Fehler an den aufrufenden Job/das SSIS-Paket weiterreichen
END CATCH
END;Das THROW am Ende ist wichtig: Der Fehler wird protokolliert, aber nicht verschluckt. So bricht der übergeordnete SSIS- oder SQL-Agent-Job kontrolliert ab und das Monitoring schlägt an.
Robustheit: Logging, Wiederanlauf und Fehlerbehandlung
Der Unterschied zwischen einer ETL-Strecke, die im Demo läuft, und einer, die jahrelang im produktiven Betrieb trägt, liegt in der Robustheit. Quellen fallen aus, Dateien kommen verspätet oder unvollständig, Netzwerke brechen ab, Datenmengen wachsen. Ein guter Ladeprozess geht davon aus, dass etwas schiefgehen wird – und ist darauf vorbereitet.
Robuster Ladelauf: jeder Schritt wird protokolliert, Erfolge werden committet, Fehler führen zu einem kontrollierten Rollback und einem Wiederanlauf ab dem letzten Checkpoint.
Durchgängiges Logging
Jeder Lauf erhält eine eindeutige Batch-ID. Über diese ID lassen sich alle Schritte, Zeilenzahlen, Laufzeiten und eventuelle Fehler einem konkreten Lauf zuordnen. Das ist die Grundlage für Monitoring, Fehlersuche und Nachweispflichten. In Projekten mit strengen Vorgaben – etwa im Banken- und Behördenumfeld – ist diese lückenlose Protokollierung nicht optional, sondern Pflicht.
Wiederanlauf und Idempotenz
Ein Ladeprozess muss nach einem Abbruch gezielt wieder anlaufen können, ohne Daten doppelt zu schreiben oder Lücken zu hinterlassen. Das erreiche ich über Checkpoints, idempotente Ladeschritte und eine Steuerungslogik, die weiß, welche Schritte bereits erfolgreich waren. Bei Informatica-Workflows habe ich genau dafür eine eigene Wiederanlauf-Logik entwickelt; im Microsoft-Umfeld setze ich auf Batch-Steuerung und transaktionssichere Prozeduren.
Steuerung über PowerShell
Rund um die eigentlichen Ladeprozesse automatisiere ich Betriebsaufgaben gern mit PowerShell – etwa das Bereitstellen von SSDT-Projekten, das Anstoßen und Überwachen von Läufen oder das Befüllen von Excel-Reports aus dem Data Warehouse. Das folgende Beispiel zeigt einen schlanken Wrapper, der eine Ladeprozedur aufruft, das Ergebnis prüft und im Fehlerfall sauber reagiert.
# Startet einen ETL-Lauf, protokolliert das Ergebnis und liefert einen
# eindeutigen Exit-Code fuer den uebergeordneten Scheduler (Jenkins/Agent).
param(
[string]$Server = "SQLPROD01",
[string]$Database = "DWH",
[int] $BatchId = (Get-Date -Format "yyyyMMddHHmm")
)
$ErrorActionPreference = "Stop"
try {
Write-Host "[$BatchId] Starte ETL-Lauf auf $Server/$Database"
Invoke-Sqlcmd -ServerInstance $Server -Database $Database `
-Query "EXEC etl.RunDailyLoad @BatchId = $BatchId;" `
-QueryTimeout 0 -ErrorAction Stop
$log = Invoke-Sqlcmd -ServerInstance $Server -Database $Database `
-Query "SELECT Status FROM etl.LoadLog WHERE BatchId = $BatchId AND Step='RunDailyLoad';"
if ($log.Status -ne "SUCCESS") {
throw "ETL-Lauf $BatchId nicht erfolgreich: $($log.Status)"
}
Write-Host "[$BatchId] ETL-Lauf erfolgreich abgeschlossen."
exit 0
}
catch {
Write-Error "[$BatchId] Fehler: $($_.Exception.Message)"
exit 1 # Scheduler erkennt den Fehlschlag und alarmiert
}Ein klarer Exit-Code ist Gold wert: Jenkins, der SQL-Agent oder ein Cron-Job erkennen damit zuverlässig, ob ein Lauf wiederholt oder eskaliert werden muss.
Datenqualität und Tests mit tSQLt
Daten, denen niemand vertraut, sind wertlos. Deshalb gehört Datenqualität für mich untrennbar zur ETL-Entwicklung. Qualität entsteht nicht durch einmalige Prüfungen, sondern durch automatisierte Tests, die bei jeder Änderung mitlaufen. Im SQL-Server-Umfeld setze ich dafür das Framework tSQLt ein, mit dem sich fachliche und technische Tests direkt in der Datenbank schreiben lassen.
Bei der Bundesagentur für Arbeit habe ich fachliche Tests und Regressionstests mit tSQLt weiterentwickelt und in eine CI/CD-Pipeline mit Jenkins eingebunden. Das bedeutet: Jede Änderung an einer Prozedur oder einem SSIS-Paket wird automatisch gegen einen definierten Satz von Erwartungswerten geprüft, bevor sie in die nächste Umgebung gelangt.
-- Test: Negative Betraege duerfen nicht in die Faktentabelle gelangen.
EXEC tSQLt.NewTestClass 'SalesTests';
GO
CREATE OR ALTER PROCEDURE SalesTests.[test negative Betraege werden gefiltert]
AS
BEGIN
-- 1) Abhaengigkeiten isolieren (Fake Table statt echter Tabelle)
EXEC tSQLt.FakeTable @TableName = N'stg.SalesOrder';
EXEC tSQLt.FakeTable @TableName = N'core.FactSales';
-- 2) Testdaten einspielen: ein gueltiger, ein ungueltiger Datensatz
INSERT INTO stg.SalesOrder (OrderId, Amount) VALUES (1, 100.00);
INSERT INTO stg.SalesOrder (OrderId, Amount) VALUES (2, -50.00);
-- 3) Zu testende Logik ausfuehren
EXEC etl.LoadSalesFact @BatchId = 999;
-- 4) Erwartung pruefen: nur der gueltige Datensatz ist angekommen
DECLARE @Cnt int = (SELECT COUNT(*) FROM core.FactSales);
EXEC tSQLt.AssertEquals @Expected = 1, @Actual = @Cnt,
@Message = N'Nur positive Betraege duerfen geladen werden.';
END;tSQLt isoliert den Test über Fake-Tables vollständig von echten Daten. Der Test ist damit reproduzierbar, schnell und unabhängig vom aktuellen Datenbestand.
Der eigentliche Gewinn automatisierter Tests zeigt sich über die Zeit. Datenstrukturen, Geschäftsregeln und Quellsysteme ändern sich; ohne Tests wird jede Anpassung zum Risiko, weil niemand mit Sicherheit sagen kann, ob eine Änderung an einer Stelle nicht an anderer Stelle etwas zerstört. Mit einer wachsenden Sammlung fachlicher Tests verwandelt sich diese Unsicherheit in Sicherheit: Schlägt nach einer Änderung ein Test fehl, ist der Fehler sofort lokalisiert, lange bevor falsche Zahlen in einem Bericht landen. Diese Sicherheit ist der eigentliche Hebel, der schnelle und gleichzeitig verlässliche Weiterentwicklung erst möglich macht.
- Plausibilitätsprüfungen direkt im Ladeprozess (Wertebereiche, Pflichtfelder, Referenzen)
- Abgleich von Quell- und Zielsummen als fachliche Kontrolle
- Regressionstests, die bei jeder Änderung automatisch ausgeführt werden
- Anonymisierung personenbezogener Daten gemäß DSGVO als fester ETL-Baustein
Die Anonymisierung verdient eine eigene Erwähnung: In mehreren Projekten habe ich Prozeduren und SSIS-Pakete entwickelt, die personenbezogene Daten gemäß DSGVO periodisch anonymisieren. Das ist ein gutes Beispiel dafür, dass ETL nicht nur Daten bewegt, sondern auch rechtliche und fachliche Anforderungen umsetzt.
Cloud-ELT mit Azure Data Factory, Synapse und Databricks
Mit der Cloud verschiebt sich das klassische ETL zunehmend zum ELT: Daten werden zunächst geladen und erst im leistungsfähigen Zielsystem transformiert. Ich habe diesen Wandel in mehreren Projekten aktiv mitgestaltet – etwa bei einem Textil- und Servicedienstleister, wo ich Ladeprozesse über Azure Synapse und Azure Data Factory aufgebaut und Daten nach Databricks in Form von Parquet- und Delta-Lake-Dateien extrahiert habe.
Azure Data Factory übernimmt dabei die Orchestrierung: Pipelines, Trigger, Parametrisierung und das Anbinden der Quellen. Die eigentliche Verarbeitung großer Datenmengen geschieht in Synapse oder in Databricks mit PySpark. Das folgende PySpark-Beispiel zeigt eine typische inkrementelle Verarbeitung mit Delta Lake, inklusive Upsert über MERGE.
# Inkrementelle Verarbeitung in Databricks: Delta-Menge lesen, bereinigen
# und per MERGE idempotent in die Zieltabelle schreiben.
from pyspark.sql import functions as F
from delta.tables import DeltaTable
# 1) Nur neue/geaenderte Saetze aus der Bronze-Schicht lesen
last_wm = spark.sql("SELECT MAX(processed_ts) AS wm FROM ctrl.watermark "
"WHERE entity = 'sales'").collect()[0]["wm"]
bronze = (spark.read.format("delta").table("bronze.sales")
.filter(F.col("modified_ts") > F.lit(last_wm)))
# 2) Fachliche Bereinigung
silver = (bronze
.filter(F.col("amount") >= 0) # keine negativen Betraege
.dropDuplicates(["order_id"]) # Deduplizierung
.withColumn("load_date", F.current_date()))
# 3) Idempotenter Upsert in die Silver-Tabelle
target = DeltaTable.forName(spark, "silver.sales")
(target.alias("t")
.merge(silver.alias("s"), "t.order_id = s.order_id")
.whenMatchedUpdateAll()
.whenNotMatchedInsertAll()
.execute())Delta Lake bringt ACID-Transaktionen in den Data Lake. Der MERGE ist idempotent: Ein erneuter Lauf mit denselben Daten ändert nichts – genau das Verhalten, das robuste Ladeprozesse brauchen.
Auch in der Cloud bleibt das Prinzip gleich: klar getrennte Schichten, zentrale Orchestrierung und durchgängiges Logging – umgesetzt mit Azure Data Factory, Synapse und Databricks.
CI/CD, Versionierung und Deployment
Moderne ETL-Entwicklung ist Softwareentwicklung. Deshalb gehören Versionsverwaltung, automatisierte Builds und kontrolliertes Deployment für mich zum Standard. Ich habe bestehende Datenbank- und SSIS-Entwicklungen in mehreren Projekten in saubere SSDT-Solutions überführt und unter Versionskontrolle gebracht – mit Git in den Ausprägungen GitHub, GitLab, Bitbucket und Azure DevOps.
Bei Danfoss Power Solutions habe ich SSIS-Pakete von SQL Server 2016 nach 2022 migriert, die Solutions in die Versionsverwaltung überführt und Azure-DevOps-Pipelines für den automatischen Build aufgebaut. Bei der Bundesagentur für Arbeit habe ich eine CI/CD-Pipeline mit Jenkins weiterentwickelt, in der tSQLt-Tests automatisch ausgeführt werden. Das folgende Beispiel zeigt den Kern einer Azure-DevOps-Pipeline für ein Datenbankprojekt.
# Build und Deployment eines SQL-Datenbankprojektes (SSDT/DACPAC).
trigger:
branches: { include: [ main ] }
pool:
vmImage: 'windows-latest'
stages:
- stage: Build
jobs:
- job: BuildDacpac
steps:
- task: VSBuild@1
displayName: 'DACPAC bauen'
inputs:
solution: '**/*.sqlproj'
configuration: 'Release'
- publish: '$(Build.SourcesDirectory)/bin/Release'
artifact: dacpac
- stage: Deploy_Test
dependsOn: Build
jobs:
- deployment: DeployTest
environment: 'DWH-Test'
strategy:
runOnce:
deploy:
steps:
- task: SqlAzureDacpacDeployment@1
displayName: 'DACPAC nach Test deployen'
inputs:
azureSubscription: 'DWH-ServiceConnection'
ServerName: 'sql-test.internal'
DatabaseName: 'DWH'
DacpacFile: '$(Pipeline.Workspace)/dacpac/DWH.dacpac'
AdditionalArguments: '/p:BlockOnPossibleDataLoss=true'Der Schalter BlockOnPossibleDataLoss verhindert, dass ein automatisches Deployment versehentlich Daten verliert. Solche Sicherungsnetze sind in produktionsnahen Pipelines unverzichtbar.
Performance und Skalierung
Performance ist in ETL-Projekten ein Dauerthema. Mit wachsenden Datenmengen reicht es nicht, Prozesse irgendwie zum Laufen zu bringen – sie müssen in ein begrenztes Ladefenster passen und dürfen den operativen Betrieb nicht stören. Ich nähere mich Performanceproblemen immer mit Messung statt mit Vermutung: Ausführungspläne, Wartestatistiken, Laufzeitprotokolle und Engpassanalysen zeigen, wo die Zeit tatsächlich verloren geht.
- Mengenbasiertes Arbeiten statt zeilenweiser Verarbeitung (Set-based statt Cursor)
- Inkrementelle Beladung statt wiederholter Full Loads
- Passende Indizierung und – bei großen Faktentabellen – Partitionierung
- Partition Switching für schnelles Laden und Archivieren großer Datenmengen
- Reduktion von Datenbewegung: Transformation dort, wo die Daten liegen
- Parallelisierung unabhängiger Ladeschritte
In einem DWH-Projekt habe ich die Partitionierung großer Tabellen samt automatischer Erweiterung der Partitionierungsfunktionen implementiert. So lassen sich neue Zeiträume laden und alte archivieren, ohne die Gesamtperformance zu beeinträchtigen. In anderen Projekten lag der Hebel im Redesign bestehender SSIS-Pakete und im Umschreiben ineffizienter SQL-Abfragen – mit unmittelbar spürbaren Laufzeitgewinnen.
Wichtig ist mir dabei ein realistischer Blick auf Performance. Nicht jeder Prozess muss maximal schnell sein; entscheidend ist, dass er verlässlich in das verfügbare Ladefenster passt und den operativen Betrieb nicht stört. Eine Optimierung, die einen Prozess von zehn auf zwei Minuten beschleunigt, ist nur dann sinnvoll, wenn die zehn Minuten ein echtes Problem waren. Deshalb priorisiere ich nach tatsächlichem Engpass: Welcher Schritt verbraucht das Ladefenster, welche Abfrage erzeugt die meisten Sperren, wo entsteht unnötige Datenbewegung? Diese gezielte Herangehensweise liefert mehr als pauschale Optimierungen, die viel Aufwand kosten und wenig bewirken.
Orchestrierung und Betrieb
Eine ETL-Strecke besteht selten aus einem einzigen Schritt. In der Realität müssen Dutzende oder Hunderte einzelner Ladeschritte in der richtigen Reihenfolge, mit den richtigen Abhängigkeiten und zur richtigen Zeit ausgeführt werden. Diese Orchestrierung ist eine eigene Disziplin. Ein falsch gesetzter Abhängigkeitspfeil oder ein vergessener Wiederanlaufpunkt kann einen ganzen Ladelauf zum Stillstand bringen.
Je nach Umfeld setze ich unterschiedliche Orchestrierungswerkzeuge ein. Im klassischen Microsoft-Umfeld steuert der SQL-Server-Agent die Jobs, oft in Kombination mit Master-Paketen in SSIS, die untergeordnete Pakete in definierten Containern aufrufen. In der Cloud übernimmt Azure Data Factory die Rolle des Dirigenten mit Pipelines, Triggern und Aktivitäten. Übergreifend nutze ich Jenkins als Orchestrierungs- und Automatisierungsschicht, insbesondere dort, wo ETL eng mit CI/CD verzahnt ist.
Steuerungstabellen als Rückgrat
Unabhängig vom Werkzeug arbeite ich gern mit Steuerungstabellen in der Datenbank. Sie halten fest, welche Schritte es gibt, in welcher Reihenfolge sie laufen, wann sie zuletzt erfolgreich waren und welche Parameter ein Lauf benötigt. Eine solche metadatengetriebene Steuerung hat einen großen Vorteil: Neue Ladeschritte lassen sich oft allein durch einen Eintrag in der Steuerungstabelle ergänzen, ohne dass Code geändert werden muss. Das reduziert Fehlerquellen und macht den Betrieb transparent.
-- Eine schlanke Steuerung liest die naechsten faelligen Ladeschritte aus
-- einer Metadatentabelle und ruft je Schritt die hinterlegte Prozedur auf.
DECLARE @Step sysname, @Proc nvarchar(256),
@BatchId bigint = NEXT VALUE FOR etl.BatchSeq;
DECLARE cur CURSOR LOCAL FAST_FORWARD FOR
SELECT StepName, ProcName
FROM etl.LoadStep
WHERE IsActive = 1
ORDER BY StepOrder;
OPEN cur;
FETCH NEXT FROM cur INTO @Step, @Proc;
WHILE @@FETCH_STATUS = 0
BEGIN
BEGIN TRY
EXEC etl.WriteLog @BatchId, @Step, N'START';
EXEC sp_executesql @stmt = N'EXEC ' + @Proc + N' @BatchId',
@params = N'@BatchId bigint', @BatchId = @BatchId;
EXEC etl.WriteLog @BatchId, @Step, N'SUCCESS';
END TRY
BEGIN CATCH
EXEC etl.WriteLog @BatchId, @Step, N'ERROR', NULL, ERROR_MESSAGE();
BREAK; -- Lauf kontrolliert anhalten; Wiederanlauf ab diesem Schritt
END CATCH
FETCH NEXT FROM cur INTO @Step, @Proc;
END;
CLOSE cur; DEALLOCATE cur;Die Steuerung ist bewusst einfach gehalten. Sie protokolliert jeden Schritt, hält bei einem Fehler kontrolliert an und erlaubt einen gezielten Wiederanlauf – ohne dass die Reihenfolge im Code festverdrahtet ist.
Zum Betrieb gehört schließlich das Monitoring. Über die Log-Tabellen lassen sich Dashboards bauen, die auf einen Blick zeigen, welche Läufe erfolgreich waren, wie lange sie gedauert haben und wo es hakt. In einem Projekt habe ich ein solches Monitoring zusätzlich mit Grafana visualisiert, in anderen Projekten genügten gezielte Abfragen und automatische Benachrichtigungen im Fehlerfall.
Vorgehen in der Zusammenarbeit
Gute ETL-Entwicklung beginnt nicht mit Code, sondern mit Verständnis. Bevor ich eine Strecke baue oder umbaue, verschaffe ich mir ein klares Bild der Quellsysteme, der fachlichen Anforderungen und des bestehenden Betriebs. Dieses Vorgehen hat sich über viele Projekte hinweg bewährt und sorgt dafür, dass die Lösung am Ende zur Realität des Unternehmens passt.
- Analyse: Quellsysteme, Datenstrukturen, Geschäftsregeln und bestehende Prozesse verstehen
- Konzept: Schichtenarchitektur, Lademuster und Fehlerstrategie festlegen
- Umsetzung: Ladeprozesse iterativ entwickeln, von Anfang an mit Tests und Logging
- Test und Abnahme: fachliche und technische Prüfung gemeinsam mit den Fachbereichen
- Betrieb: Monitoring, Wiederanlauf, Dokumentation und kontinuierliche Verbesserung
Mir ist die enge Abstimmung mit den Fachbereichen wichtig. In vielen Projekten – etwa in den Bereichen Controlling, Finance und HR – habe ich Anforderungen direkt mit den Fachabteilungen ausgearbeitet und in passende technische Lösungen übersetzt. Daten sind kein Selbstzweck; sie sollen eine fachliche Frage beantworten. Je besser ich diese Frage verstehe, desto besser wird die Lösung.
Ich arbeite sowohl remote als auch hybrid und vor Ort, allein oder als Teil eines bestehenden Teams. Über die Jahre habe ich mich in sehr unterschiedliche Branchen eingearbeitet – öffentlicher Sektor, Finanzdienstleistung, Industrie, Handel, Logistik und Versicherung. Diese Vielfalt hilft, weil sich bewährte Muster aus einer Branche oft auf eine andere übertragen lassen. Gleichzeitig respektiere ich, dass jedes Unternehmen seine eigene gewachsene Landschaft, seine eigenen Konventionen und seine eigenen Zwänge hat. Eine Lösung muss in diese Realität passen, nicht in ein Lehrbuch.
Typische Leistungen im ETL-Projekt
Je nach Projektphase und Bedarf übernehme ich unterschiedliche Aufgaben rund um die ETL-Entwicklung – von der Analyse über die Umsetzung bis zum Betrieb.
- Analyse bestehender ETL-Prozesse, Quellsysteme und Geschäftsregeln
- Entwurf einer schichtbasierten ETL-Architektur (Staging, Core, Auslieferung)
- Entwicklung von SSIS-Paketen und mengenbasierten T-SQL-Ladeprozeduren
- Inkrementelle Beladung, CDC, SCD2 und Historisierung
- Migration und Modernisierung bestehender SSIS- und ODI-Strecken
- Cloud-ELT mit Azure Data Factory, Synapse und Databricks
- Datenqualität, fachliche Tests und Regressionstests mit tSQLt
- DSGVO-konforme Anonymisierung personenbezogener Daten
- CI/CD mit Jenkins und Azure DevOps, Versionierung mit Git
- Performanceanalyse, Optimierung und Partitionierung
- Monitoring, Wiederanlauf, Fehlerbehandlung und technische Dokumentation
Ausgewählte anonymisierte Referenzprojekte
Öffentlicher Auftraggeber
Weiterentwicklung eines bestehenden Data Warehouse mit SQL Server 2019: Ausbau der ETL-Prozeduren und SSIS-Pakete, Data-Vault-Importschicht, fachliche Tests und Regressionstests mit tSQLt, Weiterentwicklung der CI/CD-Pipeline mit Jenkins, PowerShell-Automatisierung und Anonymisierung personenbezogener Daten.
Industrieunternehmen
Migration von SSIS-Paketen von SQL Server 2016 nach 2022, Überführung in die Versionsverwaltung und Aufbau von Azure-DevOps-Pipelines für den automatischen Build sowie Analyse der Anforderungen für ein automatisiertes Deployment auf On-Premises-SQL-Server.
Sparkasse / Finanzdienstleister
Ablösung einer Java-Anwendung und einer Oracle-Datenbank durch SSIS-Strecken: Entwicklung von SSIS-Paketen zum Laden von Textdateien, eines Datenbankprojektes für das Deployment auf mehreren Servern, Monitoring-Queries sowie Redesign und Performanceoptimierung bestehender Pakete.
Loyalty / Handel / Clearing
Weiterentwicklung und Stabilisierung von Clearing-Prozessen, Überführung in eine SSDT-Solution, Einführung von Bitbucket auf Git-Basis, Entwicklung von SSIS-Paketen und SSRS-Reports, Abfragen von REST- und OData-APIs, DSGVO-konforme Anonymisierung sowie Power-BI-Datenmodelle mit Row-Level-Security.
Textil- und Servicedienstleister
Weiterentwicklung eines Enterprise Operational Data Store, neue Datastores für Sales und HR, Ladeprozesse nach Dynamics 365 über Azure Synapse, Azure-Pipelines zur Orchestrierung, Extraktion nach Databricks als Parquet-/Delta-Dateien und Maßnahmen zur Kostenreduktion in Azure.
Logistik / Konzern
Entwicklung von DWH-ETL-Prozessen für Bonus- und Qualitätssicherungssysteme: transaktionsgesteuerte BTEQ-Skripte zur Datenakquise, Informatica-Mappings, -Worklets und -Workflows mit Wiederanlauf-Logik, Tuning der SQL-Statements und Monitoring der geplanten Läufe.
Häufige Fragen zur ETL-Entwicklung
Arbeiten Sie eher mit SSIS oder mit T-SQL?
Mit beidem – und vor allem mit der richtigen Kombination. SSIS ist stark beim Orchestrieren und beim Anbinden heterogener Quellen, T-SQL ist stark bei mengenbasierten Transformationen. In vielen Projekten habe ich zeilenweise SSIS-Datenflüsse durch mengenbasierte T-SQL-Logik ersetzt und damit Laufzeit und Stabilität deutlich verbessert.
Können Sie bestehende ETL-Strecken übernehmen und modernisieren?
Ja. Ein großer Teil meiner Projekte besteht aus der Übernahme, Stabilisierung und Modernisierung bestehender Ladeprozesse – inklusive Versionierung, Tests, Migration auf neue SQL-Server-Versionen und schrittweiser Überführung in die Cloud.
Wie stellen Sie sicher, dass die Daten korrekt sind?
Durch eingebaute Plausibilitätsprüfungen, durch den Abgleich von Quell- und Zielsummen und vor allem durch automatisierte fachliche Tests und Regressionstests mit tSQLt, die in die CI/CD-Pipeline eingebunden sind.
Unterstützen Sie auch Cloud-ETL beziehungsweise ELT?
Ja. Ich habe Ladeprozesse mit Azure Data Factory, Azure Synapse und Databricks aufgebaut, Daten als Parquet- und Delta-Lake-Dateien extrahiert und dabei gezielt auf Kostenkontrolle geachtet.
Wie gehen Sie mit personenbezogenen Daten um?
Anonymisierung gemäß DSGVO ist in mehreren meiner Projekte ein fester ETL-Baustein. Ich habe Prozeduren und SSIS-Pakete entwickelt, die personenbezogene Daten periodisch und nachvollziehbar anonymisieren.
In welchen Sprachen können wir zusammenarbeiten?
Auf Deutsch, Englisch und Portugiesisch – jeweils fließend, auch in technischen und fachlichen Diskussionen.