Johannes MerkelData & BI für Unternehmenssteuerung
← Alle ArtikelPower BI im Controlling

Von 6,5 Sekunden auf 0,15: Wie ein Snapshot-Pattern ein lahmes Dashboard rettet

3 Min. Lesezeit

„Das Dashboard hängt." Diese Rückmeldung kam im laufenden Reporting eines Bestandshalters. Nicht beim Refresh, sondern beim ganz normalen Klicken durch die Berichtsseite. Eine Matrix mit belegter Fläche je Wirtschaftseinheit brauchte mehrere Sekunden, bevor sie reagierte. Am Ende lag die Abfragezeit dieser einen Kennzahl bei rund 6,5 Sekunden. Nach dem Umbau: 156 Millisekunden. Der Hebel war kein schnellerer Rechner, sondern ein anderes Modellierungs-Pattern.

Die Ausgangslage

Die Kennzahl „belegte Fläche zum Stichtag" stammt aus einer Gültigkeitsbereichs-Faktentabelle: Jede Zeile trägt ein gültig-ab- und ein gültig-bis-Datum (eine SCD-Type-2-artige Historie). Es gibt bewusst keine Beziehung zur Kalenderdimension. Der Stichtag wird im Bericht gewählt.

Das zugehörige Measure löste den Bestand pro Abfrage live auf: Für jeden Stichtag bildete es per CALCULATETABLE(VALUES(<key>), gültigab ≤ Stichtag, gültigbis ≥ Stichtag) die zum Tag gültige Menge und summierte darüber. Funktional korrekt. Aber die Kosten skalieren mit der Anzahl der äußeren Gruppen: Wirtschaftseinheiten × Kennzahlen × Matrixzellen. In einer großen Matrix wird daraus sehr viel Arbeit.

Die Diagnose: erst messen, dann optimieren

Der erste Reflex. „die Measure-Köpfe sind zu kompliziert". Führte in die Irre. Die eigentliche Zeit steckte nicht in den Köpfen, sondern in den Stock-Measures dahinter, die pro Gruppenwert die Gültigkeit neu auflösten.

Sichtbar wird das nur mit dem richtigen Werkzeug:

  • DAX Studio Server Timings zeigt den hohen Anteil der Storage Engine, viele Scans und wie die Kosten mit der Zeilenzahl wachsen. Das ist die Diagnose.
  • Der VertiPaq Analyzer (.vpax) zeigt dagegen nur die Größe des Modells, nicht die Abfragezeit. Für dieses Problem das falsche Instrument.

Merksatz: Performance-Probleme misst man an der Query, nicht am Modellumfang.

Die Lösung: einmal materialisieren statt pro Abfrage rechnen

Statt den Bestand bei jeder Interaktion neu aufzulösen, wird er einmal beim Refresh vorberechnet. Als berechnete Tabelle mit dem Korn Stichtag × Faktzeile:

Konkret: eine Monatsultimo-„Spine" wird mit den jeweils gültigen Faktzeilen gekreuzt. Im Bericht filtert man den Snapshot dann nur noch per Wert auf den Stichtag (EOMONTH(<gewähltes Datum>, 0)). Aus der teuren Pro-Entity-Materialisierung wird ein einfacher Star-Join. Und der ist in der Storage Engine billig.

Die drei Stolperfallen

  1. GENERATE schleppt die Spine-Spalte mit. Kreuzt man die Monatsenden per GENERATE mit der inneren Menge, bleibt die Datums-Spalte als zusätzliche Spalte in der Tabelle. Passt nicht zur definierten Spaltenliste, die Tabelle prozessiert nicht, und die konsumierenden Measures werden plötzlich als „fehlend" gemeldet. Der Fehler sieht aus wie ein Measure-Problem, ist aber die Tabelle. Fix: ein äußeres SELECTCOLUMNS mit genau den gewünschten Spalten.

  2. Niemals eine Beziehung Snapshot ↔ Kalenderdimension. Sonst summiert ein TOTALYTD über die Monatsultimo-Snapshots und zählt den Bestand mehrfach. Die klassische Flow-vs-Stock-Doppelzählung. Der Snapshot bleibt bewusst unverbunden; der Stichtag kommt als Wertfilter.

  3. Parität nur mit Toleranz prüfen. Snapshot- und Live-Logik summieren in anderer Reihenfolge, dadurch entsteht Floating-Point-Rauschen in der Größenordnung 1e-13 bis 1e-10. Ein Vergleich auf „Differenz ≠ 0" meldet überall scheinbare Abweichungen. Richtig ist ein Vergleich auf ABS(Differenz) > 0,5: null Zeilen bedeutet, dass alt und neu praktisch identisch sind.

Der Trade-off

Kostenlos ist das nicht: Die berechnete Tabelle wird bei jedem Refresh neu materialisiert (Spine × Gültigkeits-Scans). Die Refresh-Dauer vorher und nachher messen. Wird der Snapshot zu groß (Anzahl Monate × Anzahl Entitäten), begrenzt man ihn auf die letzten N Monate oder verlagert die Materialisierung ins Data Warehouse. Sauber für die Langfrist-Architektur, aber mit Abhängigkeit zum Datenbank-Team.

Das Takeaway

Interaktive Reporting-Performance entscheidet sich selten an der Hardware und fast immer an der Frage: Rechne ich pro Abfrage oder einmal beim Refresh? Bei Stichtags-Beständen aus Gültigkeitsbereichs-Fakten ist der materialisierte Snapshot das Mittel der Wahl. Vorgehen: erst mit Server Timings diagnostizieren, dann Snapshot bauen, dann Parität mit Toleranz absichern. Der Faktor 40 ist dabei keine Ausnahme.