Extract, Transform, Load ohne externes Framework
ETL-Patterns lassen sich in weiten Teilen direkt in SQL abbilden, ohne dass jede Transformation in Anwendungscode wandert. Wer Extract, Transform und Load als Mengenoperationen begreift, baut Pipelines, die idempotent, nachvollziehbar und einfach zu testen sind, statt fragiler Skriptketten mit versteckten Seiteneffekten.
Inhaltsverzeichnis
- 1. Was ETL-Patterns wirklich loesen
- 2. Extract-Phase: Quelldaten sauber ziehen
- 3. Transform-Phase: Transformation als Mengenoperation
- 4. Load-Phase: Zieltabellen effizient befuellen
- 5. Incremental Loads vs. Full Loads
- 6. Idempotenz: Pipelines wiederholbar machen
- 7. Data Quality Checks direkt in SQL
- 8. Orchestrierung und Scheduling von ETL-Jobs
- 9. ETL-Patterns im Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Was ETL-Patterns wirklich loesen
Ein ETL-Pattern beschreibt eine wiederholbare Struktur, um Daten aus einem Quellsystem zu extrahieren, in ein Zielformat zu transformieren und in ein Zielsystem zu laden. Der Reflex vieler Entwickler ist, diese drei Schritte in Anwendungscode zu implementieren: Zeilen aus der Datenquelle lesen, in Python oder PHP transformieren, dann einzeln zurueckschreiben. Das funktioniert bei kleinen Datenmengen, skaliert aber schlecht, sobald Millionen Zeilen pro Lauf verarbeitet werden muessen, weil jede Zeile einen Roundtrip zur Datenbank verursacht.
Der robustere Ansatz verlagert moeglichst viel Logik in SQL selbst. Extract wird zu einer gefilterten SELECT-Abfrage, Transform zu einer Kombination aus CASE, COALESCE, Window Functions und Joins, Load zu einem einzigen MERGE- oder INSERT-Statement. Dieses ETL-Pattern nutzt die Mengenverarbeitung der Datenbank, statt sie Zeile fuer Zeile zu umgehen. Die folgenden Abschnitte zeigen, wie Extract, Transform und Load konkret als SQL-Patterns aussehen, wie Incremental Loads funktionieren und wie man Data Quality Checks direkt in die Pipeline einbaut.
2. Extract-Phase: Quelldaten sauber ziehen
Die Extract-Phase eines ETL-Patterns entscheidet ueber die Laufzeit der gesamten Pipeline. Ein haeufiger Fehler ist der vollstaendige Export einer Quelltabelle bei jedem Lauf, obwohl sich nur ein Bruchteil der Zeilen seit dem letzten Lauf geaendert hat. Stattdessen filtert man ueber eine Wasserzeichen-Spalte, meist ein updated_at-Zeitstempel oder eine monoton steigende ID, und liest nur Zeilen, die seit dem letzten erfolgreichen Lauf neu hinzugekommen sind. Dieses ETL-Pattern reduziert die zu uebertragende Datenmenge oft um mehr als neunzig Prozent.
Wichtig ist, das Wasserzeichen selbst persistent in einer Kontrolltabelle zu speichern, statt es im Anwendungscode zu berechnen. So bleibt die Extract-Logik idempotent und nachvollziehbar, auch wenn ein Job mehrfach fehlschlaegt und erneut gestartet wird. Bei verteilten Quellsystemen kommt zusaetzlich eine kleine Uhrzeit-Verzoegerung ins Spiel, um Clock-Skew zwischen Anwendungsserver und Datenbank abzufedern, sonst gehen Zeilen verloren, die genau zum Zeitpunkt des Extracts geschrieben wurden.
-- Incremental extract using a persisted watermark
-- Read control table for last successful extract timestamp
SELECT last_extracted_at
FROM etl_control
WHERE source_table = 'orders';
-- Extract only rows changed since the watermark,
-- with a small safety buffer for clock skew
SELECT order_id, customer_id, status, total_amount, updated_at
FROM orders
WHERE updated_at > (
SELECT last_extracted_at - INTERVAL '5 minutes'
FROM etl_control
WHERE source_table = 'orders'
)
ORDER BY updated_at ASC;
-- After a successful load, advance the watermark
UPDATE etl_control
SET last_extracted_at = (SELECT MAX(updated_at) FROM staging_orders)
WHERE source_table = 'orders';
3. Transform-Phase: Transformation als Mengenoperation
Die Transform-Phase eines ETL-Patterns lohnt sich fast immer als SQL-Query, nicht als Schleife im Anwendungscode. CASE-Ausdruecke bilden Business-Regeln ab, COALESCE ersetzt fehlende Werte durch Standardwerte, und Window Functions berechnen laufende Summen oder Rangfolgen ohne selbstgeschriebene Aggregationslogik. Der entscheidende Vorteil: Die Datenbank optimiert diese Operationen selbst und parallelisiert sie ueber mehrere Kerne, waehrend eine Anwendungsschleife immer seriell bleibt.
Ein weiteres wichtiges ETL-Pattern in der Transform-Phase ist die Trennung von Staging- und Ziel-Schema. Rohdaten landen zunaechst unveraendert in einer Staging-Tabelle, die Transformation geschieht in einem zweiten SQL-Schritt von Staging nach Ziel. Diese Trennung erlaubt es, fehlerhafte Transformationen zu debuggen, ohne die Extraktion erneut ausfuehren zu muessen, und macht jeden Schritt der Pipeline einzeln nachvollziehbar und testbar.
-- Transform staged orders into the target schema
INSERT INTO fact_orders (order_id, customer_id, status_code, revenue_eur, order_date)
SELECT
s.order_id,
s.customer_id,
CASE
WHEN s.status = 'completed' THEN 'C'
WHEN s.status = 'cancelled' THEN 'X'
ELSE 'P'
END AS status_code,
COALESCE(s.total_amount, 0) * COALESCE(fx.rate_to_eur, 1) AS revenue_eur,
CAST(s.updated_at AS DATE) AS order_date
FROM staging_orders s
LEFT JOIN fx_rates fx ON fx.currency = s.currency
AND fx.rate_date = CAST(s.updated_at AS DATE);
4. Load-Phase: Zieltabellen effizient befuellen
Die Load-Phase ist der Schritt, bei dem viele selbstgebaute ETL-Loesungen ineffizient werden. Einzelne INSERT-Statements pro Zeile erzeugen bei jedem Aufruf einen eigenen Transaktions-Overhead und sind bei grossen Datenmengen um Groessenordnungen langsamer als Batch-Operationen. Das produktionstaugliche ETL-Pattern ist ein einziges MERGE-Statement, das INSERT und UPDATE in einer Anweisung kombiniert und dabei die komplette Batch-Groesse in einer Transaktion verarbeitet.
MERGE (in MySQL als INSERT ... ON DUPLICATE KEY UPDATE emuliert) vergleicht Quelldaten gegen Zieldaten anhand eines Schluessels: existiert die Zeile bereits, wird sie aktualisiert, andernfalls neu eingefuegt. Dieses ETL-Pattern macht die Load-Phase automatisch idempotent, weil ein erneuter Lauf mit denselben Daten keine Duplikate erzeugt, sondern lediglich denselben Zustand erneut schreibt.
-- PostgreSQL: MERGE combines insert and update in a single statement
MERGE INTO fact_orders AS target
USING staging_orders_transformed AS source
ON target.order_id = source.order_id
WHEN MATCHED THEN
UPDATE SET
status_code = source.status_code,
revenue_eur = source.revenue_eur
WHEN NOT MATCHED THEN
INSERT (order_id, customer_id, status_code, revenue_eur, order_date)
VALUES (source.order_id, source.customer_id, source.status_code,
source.revenue_eur, source.order_date);
-- MySQL equivalent: upsert via ON DUPLICATE KEY UPDATE
INSERT INTO fact_orders (order_id, customer_id, status_code, revenue_eur, order_date)
VALUES (1001, 42, 'C', 129.90, '2026-07-30')
ON DUPLICATE KEY UPDATE
status_code = VALUES(status_code),
revenue_eur = VALUES(revenue_eur);
5. Incremental Loads vs. Full Loads
Ein Full Load laedt bei jedem Lauf den kompletten Datensatz neu, was einfach zu implementieren, aber bei wachsenden Tabellen zunehmend teuer ist. Ein Incremental Load, das zentrale ETL-Pattern fuer wachsende Datenmengen, verarbeitet nur Zeilen, die sich seit dem letzten Lauf geaendert haben. Die Entscheidung zwischen beiden Ansaetzen haengt von der Groesse der Quelltabelle und der Aenderungsrate ab: bei kleinen Referenztabellen mit seltenen Aenderungen ist ein Full Load oft einfacher und robuster als die zusaetzliche Komplexitaet eines Wasserzeichens.
Bei grossen Faktentabellen mit Millionen Zeilen pro Tag ist Incremental Load praktisch alternativlos. Wichtig ist dabei, geloeschte Zeilen im Quellsystem zu beruecksichtigen: ein reiner updated_at-Filter erkennt keine Hard-Deletes. Dafuer braucht es entweder Soft-Deletes im Quellsystem (ein deleted_at-Flag statt physischem Loeschen) oder einen periodischen Abgleich der vollstaendigen Schluesselmenge zwischen Quelle und Ziel.
6. Idempotenz: Pipelines wiederholbar machen
Idempotenz ist die Eigenschaft eines ETL-Patterns, bei mehrfacher Ausfuehrung mit denselben Eingabedaten immer dasselbe Ergebnis zu erzeugen. Das ist keine akademische Feinheit, sondern eine harte Anforderung fuer Produktionspipelines, weil Jobs regelmaessig fehlschlagen und erneut gestartet werden. Ein nicht idempotentes Pipeline-Design erzeugt bei einem Retry doppelte Zeilen oder inkonsistente Summen, ein idempotentes Design produziert exakt denselben Endzustand.
Die Kombination aus MERGE-Upserts, transaktionaler Verarbeitung ganzer Batches und einer Kontrolltabelle fuer den letzten erfolgreichen Lauf macht ein ETL-Pattern idempotent. Jeder Batch wird in einer einzigen Transaktion verarbeitet: entweder committen alle Zeilen des Batches inklusive Wasserzeichen-Update, oder keine. Ein abgebrochener Job hinterlaesst so nie einen halb verarbeiteten Zustand, der die naechste Ausfuehrung verfaelscht.
-- Idempotent batch: staging, transform, load and watermark
-- update all commit together, or none at all
BEGIN;
TRUNCATE TABLE staging_orders_batch;
INSERT INTO staging_orders_batch
SELECT * FROM staging_orders
WHERE batch_id = 20260730;
MERGE INTO fact_orders AS target
USING staging_orders_batch AS source
ON target.order_id = source.order_id
WHEN MATCHED THEN UPDATE SET revenue_eur = source.total_amount
WHEN NOT MATCHED THEN INSERT (order_id, revenue_eur)
VALUES (source.order_id, source.total_amount);
UPDATE etl_control
SET last_extracted_at = NOW(), last_batch_id = 20260730
WHERE source_table = 'orders';
COMMIT;
7. Data Quality Checks direkt in SQL
Fehlerhafte Daten, die unbemerkt in ein Data Warehouse gelangen, kosten spaeter ein Vielfaches der Zeit, die eine fruehe Pruefung gekostet haette. Ein bewaehrtes ETL-Pattern baut Data Quality Checks direkt zwischen Transform und Load ein: Anzahlvergleiche zwischen Quelle und Staging, Nullwert-Pruefungen auf Pflichtfelder und Bereichspruefungen auf plausible Wertebereiche. Schlaegt eine Pruefung fehl, bricht die Pipeline kontrolliert ab, statt fehlerhafte Daten stillschweigend weiterzureichen.
Diese Pruefungen lassen sich vollstaendig in SQL formulieren und muessen nicht in einem separaten Tool laufen. Eine einfache Zaehl-Abfrage vergleicht die Zeilenanzahl vor und nach der Transformation, eine Aggregation prueft, ob die Summe der Umsaetze innerhalb einer erwarteten Bandbreite liegt. Dieses ETL-Pattern macht Datenqualitaet zu einem festen, automatisierten Bestandteil jedes Laufs statt einer manuellen Stichprobe.
-- Row count parity check between staging and target
SELECT
(SELECT COUNT(*) FROM staging_orders_batch) AS staged_count,
(SELECT COUNT(*) FROM fact_orders WHERE order_date = CURRENT_DATE) AS loaded_count;
-- Null check on mandatory columns
SELECT COUNT(*) AS bad_rows
FROM staging_orders_batch
WHERE customer_id IS NULL OR total_amount IS NULL;
-- Plausibility check: revenue must stay within an expected range
SELECT SUM(revenue_eur) AS daily_revenue
FROM fact_orders
WHERE order_date = CURRENT_DATE
HAVING SUM(revenue_eur) NOT BETWEEN 1000 AND 500000;
8. Orchestrierung und Scheduling von ETL-Jobs
Ein einzelnes ETL-Pattern loest selten das gesamte Problem, meist muessen mehrere Extract-Transform-Load-Schritte in der richtigen Reihenfolge und mit Abhaengigkeiten zueinander laufen. Orchestrierungswerkzeuge wie Airflow, Dagster oder einfache Cron-Jobs mit SQL-Skripten uebernehmen das Scheduling, die eigentliche Datenverarbeitung bleibt aber in SQL. Wichtig ist, dass jeder Schritt einzeln wiederholbar ist, damit ein Fehlschlag in Schritt drei nicht automatisch Schritt eins und zwei erneut ausfuehrt.
Ein einfaches, aber robustes ETL-Pattern fuer die Orchestrierung ist eine Kontrolltabelle mit Status pro Batch: pending, running, success, failed. Jeder Orchestrierungs-Lauf prueft zuerst den Status des vorherigen Laufs, bevor er einen neuen startet, und verhindert so parallele, konkurrierende Ausfuehrungen desselben Jobs. Diese Statustabelle ist gleichzeitig die zentrale Stelle fuer Monitoring und Alerting bei fehlgeschlagenen Laeufen.
9. ETL-Patterns im Vergleich
Die Wahl des richtigen ETL-Patterns haengt stark vom Datenvolumen, der Latenzanforderung und der Komplexitaet der Transformation ab. Die folgende Tabelle stellt die gaengigen Ansaetze gegenueber.
| Pattern | Latenz | Komplexitaet | Typischer Einsatz |
|---|---|---|---|
| Full Load | Hoch | Niedrig | Kleine Referenztabellen |
| Incremental Load | Mittel | Mittel | Grosse Faktentabellen |
| ETL (Transform vor Load) | Mittel | Mittel bis hoch | Klassische Data Warehouses |
| ELT (Transform nach Load) | Niedrig | Niedrig bis mittel | Cloud Data Warehouses mit viel Rechenleistung |
| CDC-basierter Stream | Sehr niedrig | Hoch | Nahezu Echtzeit-Reporting |
ELT gewinnt in Cloud-Data-Warehouses zunehmend an Bedeutung, weil dort Rechenleistung fuer Transformationen guenstiger und elastischer verfuegbar ist als in einem separaten ETL-Server. Das ETL-Pattern bleibt dabei konzeptionell gleich, nur die Reihenfolge von Transform und Load vertauscht sich: Rohdaten landen zuerst unveraendert im Zielsystem, die Transformation geschieht danach als SQL-View oder materialisierte Tabelle im selben System.
Mironsoft
Data Engineering, SQL-Pipelines und Warehouse-Architektur
ETL-Pipelines, die zuverlaessig und idempotent laufen?
Wir bauen SQL-basierte ETL-Pipelines mit Incremental Loads, MERGE-Upserts und automatisierten Data Quality Checks, die auch bei Retries konsistent bleiben.
Pipeline-Design
Extract, Transform und Load als idempotente SQL-Schritte konzipieren
Data Quality
Automatisierte Pruefungen zwischen Transform und Load einbauen
Orchestrierung
Scheduling und Monitoring fuer bestehende ETL-Jobs nachruesten
10. Zusammenfassung
Die wichtigsten ETL-Patterns mit SQL reduzieren eine komplexe Datenverarbeitung auf wenige, klar abgegrenzte Mengenoperationen. Extract liest ueber ein Wasserzeichen nur geaenderte Zeilen, Transform bildet Business-Regeln als CASE, COALESCE und Window Functions ab, Load schreibt ueber MERGE idempotent in die Zieltabelle. Data Quality Checks zwischen Transform und Load verhindern, dass fehlerhafte Daten unbemerkt weiterverarbeitet werden.
Der groesste Hebel liegt darin, so viel Logik wie moeglich in der Datenbank zu belassen, statt sie in Anwendungscode auszulagern. Ein SQL-basiertes ETL-Pattern ist einfacher zu testen, einfacher zu optimieren und profitiert automatisch von jeder Verbesserung des Datenbank-Optimizers, ohne dass eine einzige Zeile Anwendungscode angefasst werden muss.
ETL-Patterns mit SQL — Das Wichtigste auf einen Blick
Extract
Wasserzeichen-Filter auf updated_at statt vollstaendigem Export, persistiert in einer Kontrolltabelle.
Transform
CASE, COALESCE und Window Functions als reine Mengenoperationen statt Zeilenschleife.
Load
MERGE beziehungsweise ON DUPLICATE KEY UPDATE fuer idempotente Upserts in einer Transaktion.
Qualitaet & Orchestrierung
Automatisierte Zaehl- und Plausibilitaetschecks, Statustabelle fuer wiederholbare Batches.