📚 Qlik Sense Kurs – Artikel 7 von 28
← Vorheriger Artikel: Daten transponieren – CROSSTABLE vs GENERIC LOAD
→ Nächster Artikel: QVD optimierung
Was kann man in 16 Minuten über Incremental Loading lernen?
Das tägliche Neuladen von Millionen von Datensätzen ist langsam, ineffizient und belastet Ihre Quellsysteme unnötig. In diesem Guide lernen Sie die wichtigste Technik für professionelle Qlik-Anwendungen: das inkrementelle Laden. Statt alles neu zu laden, laden Sie nur noch die Daten, die sich seit dem letzten Mal geändert haben.
- Das Delta-Load-Pattern implementieren und so Ladezeiten um bis zu 95% reduzieren.
- Einen robusten Prozess für Inserts, Updates und Deletes mit Timestamp-Tracking und QVD-Merge erstellen.
- Fallback-Strategien für ausfallsichere Ladevorgänge entwickeln, die auch bei Fehlern zuverlässig sind.
Zeitinvestition: 16 Min Lesen + 3 Std Hands-on
Voraussetzung: QVD-Optimierung verstanden
Wie funktioniert der Timestamp-basierte Delta Load im Incremental Loading?
Das Problem: Ihre Datenladung dauert zwei Stunden für 10 Millionen Datensätze, obwohl sich täglich nur etwa 1% der Daten ändert.
Die Analogie: Ein Full Load ist, als würde man jeden Tag ein 1000-seitiges Buch komplett neu kopieren. Ein Incremental Load ist, als würde man nur die 10 Seiten kopieren, die seit gestern geändert wurden, und sie im bestehenden Buch austauschen.
Die Lösung in 4 logischen Schritten:
- Nachsehen: Wir lesen aus einer Kontroll-QVD, wann der letzte erfolgreiche Ladevorgang war.
- Nur Neues laden: Wir fragen die Datenquelle nur nach Datensätzen, die neuer sind als dieser letzte Zeitstempel.
- Zusammenführen: Wir laden unsere bestehenden historischen Daten aus einer QVD (ohne die Datensätze, die wir gerade aktualisieren) und hängen die neuen/geänderten Daten an.
- Notieren: Nach dem erfolgreichen Speichern der neuen kombinierten QVD aktualisieren wir unsere Kontroll-QVD mit dem heutigen Datum für den nächsten Ladevorgang.
Hier ist der vollständige Code, der diese Logik umsetzt.
LET vLastLoadDate = Date("2000-01-01");
IF FileSize("lib://Control/LastLoad.qvd") > 0 THEN
LastLoad:
LOAD MaxDate as LastLoadDate FROM [lib://Control/LastLoad.qvd] (qvd);
LET vLastLoadDate = Date(Peek("LastLoadDate", 0, "LastLoad"));
DROP TABLE LastLoad;
END IF
IncrementalData:
LOAD
CustomerID,
CustomerName,
Region,
ModifiedDate
FROM [lib://Source/customers.csv]
(txt, utf8, embedded labels, delimiter is ";")
WHERE Date(Date#(ModifiedDate, "YYYY-MM-DD")) > "$(vLastLoadDate)";
IF NoOfRows("IncrementalData") > 0 THEN
CONCATENATE (IncrementalData)
LOAD *
FROM [lib://QVDs/Customers.qvd] (qvd)
WHERE NOT EXISTS(CustomerID);
STORE IncrementalData INTO [lib://QVDs/Customers.qvd] (qvd);
NewLastLoad:
LOAD Today() as MaxDate AUTOGENERATE 1;
STORE NewLastLoad INTO [lib://Control/LastLoad.qvd] (qvd);
DROP TABLE NewLastLoad;
END IF
Erklärung der Schlüsselbefehle:
Peek("LastLoadDate", 0, "LastLoad"): Holt den Wert aus der ersten Zeile der Kontrolltabelle.WHERE Date(...) > "$(vLastLoadDate)": Dies ist der eigentliche Delta-Filter, der nur neue Datensätze aus der Quelle liest.WHERE NOT EXISTS(CustomerID): Der wichtigste Befehl beim Zusammenführen. Er lädt aus der alten QVD nur die Kunden, die nicht in unserem neuenIncrementalData-Set enthalten sind, und verhindert so Duplikate.
Checkpoint: Überprüfen Sie die Zeitstempel in Ihrer finalen Customers.qvd. Werden nur neue Datensätze hinzugefügt, ohne Duplikate zu erzeugen? Erfolg!
Was sind die Incremental-Strategien in der Matrix?
Die Wahl der richtigen Strategie hängt davon ab, wie Ihr Quellsystem Änderungen nachverfolgt.
| Strategie | Änderungserkennung | Komplexität | Performance | Ideal für… |
|---|---|---|---|---|
| Timestamp-basiert | Feld «ModifiedDate» | Niedrig | Sehr gut | Standardfall für Datenbanken, APIs und Log-Dateien. |
| Hash-Vergleich | Hash («Fingerabdruck») der Zeile | Mittel | Gut | Quellen ohne Änderungsdatum (z.B. CSV-Exporte). |
| CDC (Change Data Capture) | Datenbank-Transaktionslog | Hoch | Exzellent | Enterprise-Datenbanken, die eine exakte Nachverfolgung aller Änderungen (inkl. Deletes) ermöglichen. |
Was ist Timestamp-basiertes Laden im Detail?
Wie gehe ich mit Inserts, Updates und Deletes um?
Ein robuster inkrementeller Prozess muss alle drei Änderungsarten (CRUD: Create, Read, Update, Delete) verarbeiten können. Die Logik trennt die neuen Daten in drei Kategorien, bevor sie mit dem bestehenden QVD-Bestand zusammengeführt werden.
Zuerst laden wir alle Änderungen seit dem letzten Ladevorgang und kategorisieren sie.
AllChanges:
LOAD
CustomerID,
CustomerName,
Status,
If(Status = "DELETED", "DELETE",
If(Exists(CustomerID, CustomerID), "UPDATE", "INSERT")) as ChangeType
FROM [lib://Source/customers.csv]
(txt, utf8, embedded labels, delimiter is ";")
WHERE Date(Date#(ModifiedDate, "YYYY-MM-DD hh:mm:ss")) > "$(vLastLoad)";
Danach erfolgt der Merge-Prozess. Wir laden zuerst die bestehenden Daten und schließen dabei alle Datensätze aus, die in unserem AllChanges-Set enthalten sind (sowohl Updates als auch Deletes). Danach hängen wir alle neuen und geänderten Datensätze an, aber explizit nicht die als «DELETE» markierten.
IF NoOfRows("AllChanges") > 0 THEN
ExistingData:
LOAD *
FROM [lib://QVDs/Customers.qvd] (qvd)
WHERE NOT EXISTS(CustomerID);
CONCATENATE (ExistingData)
LOAD CustomerID, CustomerName, Status
RESIDENT AllChanges
WHERE ChangeType <> "DELETE";
STORE ExistingData INTO [lib://QVDs/Customers.qvd] (qvd);
DROP TABLE ExistingData;
END IF
Was ist Hash-basierte Änderungserkennung bei Incremental Loading?
Was passiert, wenn ein Änderungsdatum fehlt bei Incremental Loading?
Was tun, wenn Ihre Datenquelle kein «ModifiedDate»-Feld hat? Die Lösung ist ein Hash-Vergleich. Ein Hash ist ein einzigartiger «Fingerabdruck», der aus den Werten einer Zeile generiert wird. Ändert sich auch nur ein Zeichen in der Zeile, ändert sich auch der Hash.
Der Prozess:
- Laden Sie die aktuellen Daten und berechnen Sie für jede Zeile einen Hash.
- Laden Sie die gespeicherten Hashes vom letzten Ladevorgang.
- Vergleichen Sie die Hashes. Eine Zeile ist neu oder geändert, wenn ihr Hash nicht in der alten Hash-Liste existiert oder sich von dem alten Hash unterscheidet.
- Speichern Sie die neue, vollständige Hash-Liste für den nächsten Ladevorgang.
Wir berechnen einen RowHash für jede Zeile der aktuellen Daten.
CurrentData:
LOAD
CustomerID,
CustomerName,
Region,
Hash128(CustomerID, CustomerName, Region) as RowHash
FROM [lib://Source/customers_without_timestamp.csv];
Wir laden die alten Hashes und vergleichen sie mit den neuen. Nur die geänderten Datensätze werden in der Tabelle ChangedRecords gespeichert, die dann weiterverarbeitet wird.
ExistingHashes:
LOAD CustomerID, RowHash as OldRowHash FROM [lib://Control/CustomerHashes.qvd] (qvd);
LEFT JOIN (CurrentData)
LOAD CustomerID, OldRowHash RESIDENT ExistingHashes;
DROP TABLE ExistingHashes;
ChangedRecords:
LOAD CustomerID, CustomerName, Region
RESIDENT CurrentData
WHERE IsNull(OldRowHash) OR RowHash <> OldRowHash;
Wie funktioniert Ausfallsicherheit und Monitoring beim Incremental Loading?
Wie funktioniert der Fallback auf Full Load bei Incremental Loading?
Ein produktionsreifes Skript muss auf Fehler reagieren können. Ein gängiges Muster ist der «Fallback auf einen Full Load». Wenn der inkrementelle Teil des Skripts fehlschlägt, wird automatisch ein sicherer, vollständiger Ladevorgang gestartet.
Wir setzen ErrorMode = 0, damit das Skript bei einem Fehler nicht abbricht. Nach dem inkrementellen Ladeversuch prüfen wir die Systemvariable ScriptError. Wenn diese nicht leer ist, ist ein Fehler aufgetreten, und wir leiten den Full Load ein.
SET ErrorMode = 0;
/* ... Ihr inkrementeller Ladecode hier ... */
IF ScriptError <> "" THEN
TRACE ERROR in incremental load: $(ScriptError);
TRACE Falling back to full load due to errors;
FullLoadFallback:
LOAD * FROM [lib://Source/customers.csv];
STORE FullLoadFallback INTO [lib://QVDs/customers.qvd] (qvd);
END IF
Wie behebe ich Probleme beim Incremental Loading – Nur Changes laden?
Wie gehe ich mit Duplikaten nach dem Ladevorgang um?
Problem: Nach einem inkrementellen Ladevorgang haben Sie doppelte Datensätze in Ihrer QVD.
Ursache: Dies ist fast immer ein Fehler in der WHERE NOT EXISTS(Schlüsselfeld)-Logik. Die häufigsten Gründe sind:
- Das Schlüsselfeld in der neuen Tabelle hat einen anderen Namen als in der alten QVD.
- Die Datentypen des Schlüsselfeldes stimmen nicht überein (z.B. Zahl vs. Text).
- Groß-/Kleinschreibung oder Leerzeichen im Schlüssel führen dazu, dass der `EXISTS`-Check fehlschlägt.
Lösung: Stellen Sie sicher, dass Ihr Primärschlüssel in beiden Datensätzen (neue Daten und alte QVD) absolut identisch ist, indem Sie ihn standardisieren, z.B. mit Upper(Trim(CustomerID)) as CustomerID.
Warum ist die Performance schlechter als bei Full Load?
Problem: Der inkrementelle Ladevorgang dauert überraschenderweise länger als ein normaler Full Load.
Ursache: Dies kann passieren, wenn der Anteil der täglichen Änderungen sehr hoch ist (z.B. > 30-40%). Der Aufwand für das Lesen der alten QVD, den `NOT EXISTS`-Vergleich und das Zusammenführen kann dann größer sein als das einfache Überschreiben durch einen Full Load.
Lösung: Implementieren Sie eine dynamische Logik. Messen Sie die Anzahl der Änderungen. Wenn die Anzahl einen bestimmten Schwellenwert überschreitet, führen Sie stattdessen einen Full Load durch.
Was sind die nächsten Schritte im Kurs zum Thema Incremental Loading?
Als nächstes: Nächstes Thema im Kurs
Verwandte Themen: