📚 Qlik Sense Kurs – Artikel 11 von 28
← Vorheriger Artikel: Star Schema in Qlik – Performance & Klarheit
→ Nächster Artikel: Link Tables für Many-to-Many Beziehungen
Fact vs Dimension – Design-Entscheidungen
Was ist der Unterschied zwischen Fact und Dimension? Facts enthalten transaktionale, messbare Daten (Umsätze, Mengen) – sie beantworten «Was ist passiert?». Dimensionen enthalten beschreibende Daten (Kunde, Produkt) – sie beantworten «Wer/Was/Wo/Wann?».
Was lernst Du über Fact vs Dimension und Design-Entscheidungen?
Nach diesem Guide kannst Du:
- Entscheiden ob Daten in eine Fact- oder Dimension-Tabelle gehören
- Die richtige Granularität für Facts definieren
- Performance-optimale Datenmodelle designen
Zeitinvestition: 15 Min Lesen + 2 Std Hands-on
Voraussetzung: Kenntnisse in Star Schema
Quick Win: In 10 Minuten weißt Du wie Du jede Tabelle richtig klassifizierst
Die Entscheidung: Fact oder Dimension?
Das Problem: Du hast eine Tabelle mit Umsatzdaten geladen und bist Dir nicht sicher: Solltest Du alle Felder in einer Tabelle lassen oder aufteilen in Facts und Dimensionen?
Die Lösung: Nutze einen einfachen Decision Tree: Sind die Daten transaktional und ändern sich häufig? → Fact. Beschreiben sie Entitäten und sind relativ stabil? → Dimension.
Wie wendet man Decision Tree in der Praxis an?
Stelle Dir diese drei Fragen:
1. Sind die Daten transaktional?
- Ja → Wahrscheinlich eine Fact-Tabelle
- Nein → Weiter zu Frage 2
2. Enthalten die Daten numerische Werte die aggregiert werden?
- Ja (Sum, Avg, Count) → Fact-Tabelle
- Nein → Dimension-Tabelle
3. Wie viele Zeilen hat die Tabelle?
- Millionen von Zeilen → Fact-Tabelle
- Tausende bis Hunderttausende → Dimension-Tabelle
Wie klassifiziert man Umsatzdaten in einem Beispiel?
// Original-Tabelle: Sales.csv
// OrderID, CustomerID, CustomerName, Region, ProductID, ProductName,
// Category, Amount, Quantity, OrderDate
// Was gehört wohin?
Analyse:
- OrderID: Eindeutiger Schlüssel → Fact (Primary Key)
- CustomerID: Referenz → Fact (Foreign Key zur Dimension)
- CustomerName, Region: Beschreibend → Dimension
- ProductID: Referenz → Fact (Foreign Key zur Dimension)
- ProductName, Category: Beschreibend → Dimension
- Amount, Quantity: Measures zum Aggregieren → Fact
- OrderDate: Zeitstempel der Transaktion → Fact
Wie wählt man die richtige Struktur für Fact und Dimension aus?
// Fact-Tabelle: Nur Transaktionen + Keys + Measures
Facts_Sales:
LOAD
OrderID, // Primary Key
CustomerID, // Foreign Key
ProductID, // Foreign Key
Date(OrderDate) as OrderDate, // Foreign Key zur Date-Dimension
Amount, // Measure
Quantity // Measure
FROM [DataSales.csv]
(ooxml, embedded labels);
Erklärung: Die Fact-Tabelle enthält nur das Skelett der Transaktion – Schlüssel und Zahlen. Keine beschreibenden Texte.
// Dimension: Kunden-Attribute
Dim_Customer:
LOAD DISTINCT
CustomerID, // Primary Key
CustomerName,
Region,
Country,
City
FROM [DataSales.csv]
(ooxml, embedded labels);
Erklärung: Die Dimension enthält alle Informationen ÜBER den Kunden. Einmal pro Kunde, nicht bei jeder Bestellung wiederholt.
// Dimension: Produkt-Attribute
Dim_Product:
LOAD DISTINCT
ProductID, // Primary Key
ProductName,
Category,
SubCategory,
Brand
FROM [DataSales.csv]
(ooxml, embedded labels);
Erklärung: Auch hier: Alle Informationen ÜBER das Produkt, einmal pro Produkt.
Performance-Tipp: Mit dieser Aufteilung sparst Du 40-60% Speicher! Die Namen «Samsung Galaxy» und «Electronics» werden nicht millionenfach wiederholt, sondern stehen nur einmal in der Dimension.
Wie verstehe und wähle ich die richtige Granularität?
Die Granularität bestimmt den «Detail-Level» Deiner Fact-Tabelle. Das ist eine der wichtigsten Design-Entscheidungen!
Faustregel: Lade Daten so detailliert wie möglich (atomic level). Aggregieren kannst Du später immer noch – aber aus aggregierten Daten kannst Du keine Details mehr rekonstruieren!
Was sind die Granularitäts-Levels im Beispiel von Fact vs Dimension?
Option 1: Order-Level (Bestellungs-Ebene)
// Ein Record = eine Bestellung
Facts_Orders:
LOAD
OrderID, // Key
CustomerID,
OrderDate,
OrderTotalAmount, // Summe aller Positionen
OrderItemCount // Anzahl Artikel
FROM Orders;
Was Du damit analysieren kannst: Umsatz pro Bestellung, Durchschnittliche Bestellgröße
Was NICHT geht: Welche Produkte wurden in dieser Bestellung gekauft?
Option 2: Order-Line-Level (Positions-Ebene)
// Ein Record = eine Position in einer Bestellung
Facts_OrderLines:
LOAD
OrderLineID, // Eindeutiger Key
OrderID, // Bestellungs-Referenz
ProductID, // Welches Produkt?
CustomerID,
OrderDate,
LineAmount, // Betrag dieser Position
LineQuantity // Menge dieser Position
FROM OrderLines;
Was Du damit analysieren kannst: Alles von Option 1 (durch Aggregation) PLUS Produktanalysen, Cross-Selling, Warenkorbanalysen
Trade-off: Mehr Zeilen = mehr Speicher (aber dafür maximale Flexibilität)
Option 3: Tages-Aggregat (voraggregiert)
// Ein Record = ein Produkt pro Tag
Facts_DailySales:
LOAD
ProductID,
Date,
Sum(Amount) as DailyAmount,
Sum(Quantity) as DailyQuantity
FROM OrderLines
GROUP BY ProductID, Date;
Was Du damit analysieren kannst: Tagesumsätze, Trends
Was NICHT geht: Kundenanalysen, einzelne Bestellungen
Best Practice: Starte mit der detailliertesten Ebene (Order-Line). Wenn Performance-Probleme auftreten, kannst Du später immer noch eine aggregierte Tabelle zusätzlich (!) erstellen.
Wie vermeidet man typische Fehler bei Fact vs Dimension Design-Entscheidungen?
⚠️ Fehler 1: Measures in Dimensionen
Symptom: Deine Summen stimmen nicht, Zahlen werden mehrfach gezählt.
Falsch:
Dim_Product:
LOAD
ProductID,
ProductName,
Category,
AveragePrice, // ← FALSCH! Das ist ein Measure
TotalSold // ← FALSCH! Das ist ein Measure
FROM Products;
Richtig:
Dim_Product:
LOAD
ProductID,
ProductName,
Category,
StandardPrice // ← OK: Ein fester Attributwert, kein Aggregat
FROM Products;
// Im Frontend berechnest Du dann:
// Durchschnittspreis: Avg(Price)
// Gesamtverkauf: Sum(Quantity)
Warum ist das wichtig? Wenn Du Measures in Dimensionen packst, werden sie bei Joins vervielfacht und Deine Summen sind falsch.
⚠️ Fehler 2: Zu viele Felder in Facts
Symptom: Synthetic Keys ($Syn-Tabellen) im Datenmodell, Performance-Probleme.
Falsch:
Facts_Sales:
LOAD
OrderID,
CustomerID,
CustomerName, // ← Redundant! Gehört in Dimension
Region, // ← Redundant! Gehört in Dimension
ProductID,
ProductName, // ← Redundant! Gehört in Dimension
Amount
FROM Sales;
Richtig:
Facts_Sales:
LOAD
OrderID,
CustomerID, // Nur die ID
ProductID, // Nur die ID
Amount
FROM Sales;
// Namen und Attribute kommen aus den Dimensionen
Warum ist das wichtig? Wenn CustomerName sowohl in Facts als auch in der Customer-Dimension vorkommt, erstellt Qlik einen Synthetic Key. Das ist ineffizient und kann zu Problemen führen.
⚠️ Fehler 3: Falsche Granularität
Symptom: Du kannst nicht alle Business-Fragen beantworten oder hast explodierende Datenmengen.
Problem: Du lädst Daten auf Order-Ebene, brauchst aber Produkt-Details:
// Zu grob!
Facts_Orders:
LOAD
OrderID,
OrderTotalAmount // Nur Gesamtsumme, keine Produkt-Info
FROM Orders;
// Frage: "Welche Produkte wurden am meisten verkauft?"
// Antwort: Unmöglich zu beantworten!
Lösung: Lade auf der detailliertesten benötigten Ebene:
// Richtig detailliert
Facts_OrderLines:
LOAD
OrderLineID,
OrderID,
ProductID, // Jetzt kannst Du nach Produkt analysieren
LineAmount,
LineQuantity
FROM OrderLines;
Was sind die Unterschiede bei der Kombination von Granularitäten in Advanced Patterns?
Manchmal hast Du Daten mit verschiedenen Detail-Levels. Beispiel: Budget ist auf Regions-Ebene, Sales auf Kunden-Ebene.
Die Herausforderung: Wie vergleichst Du Budget (pro Region) mit Sales (pro Kunde)?
Wie verwendet man Generic Keys in der Lösung?
// Sales auf Kunden-Ebene
Facts_Sales:
LOAD
OrderID,
CustomerID & '|Customer' as SalesKey, // Generic Key mit Typ-Kennzeichen
ProductID,
Date(OrderDate) as OrderDate,
Amount,
Quantity
FROM Sales;
// Budget auf Regions-Ebene
CONCATENATE(Facts_Sales)
LOAD
BudgetID,
Region & '|Region' as SalesKey, // Generic Key mit Typ-Kennzeichen
ProductID,
Date(BudgetMonth) as OrderDate,
BudgetAmount as Amount,
Null() as Quantity, // Kein Quantity bei Budget
1 as IsBudget // Flag zur Unterscheidung
FROM Budget;
Erklärung: Durch das Anhängen von ‚|Customer‘ bzw. ‚|Region‘ machst Du die Keys eindeutig. So können beide Granularitäten koexistieren.
// Erweiterte Customer-Dimension
Dim_Customer:
LOAD
CustomerID & '|Customer' as SalesKey, // Passend zum Fact
CustomerID,
CustomerName,
Region,
Country
FROM Customers;
// Regions-Level hinzufügen
CONCATENATE(Dim_Customer)
LOAD DISTINCT
Region & '|Region' as SalesKey, // Passend zum Budget-Fact
Region,
Country
RESIDENT Dim_Customer;
Erklärung: Die Dimension enthält jetzt sowohl Kunden-Einträge (detailliert) als auch Regions-Einträge (aggregiert). So können beide Fact-Granularitäten damit arbeiten.
Ergebnis: Du kannst jetzt Sales und Budget vergleichen, obwohl sie unterschiedliche Granularität haben. Qlik aggregiert automatisch richtig!
Wie beeinflussen Deine Entscheidungen die Performance?
Deine Design-Entscheidungen haben massiven Einfluss auf Performance:
| Design | Ladezeit (1M Zeilen) | Speicher | Expression-Speed |
|---|---|---|---|
| Star Schema (Facts + Dims) | 15-25 Sek | ~150-200 MB | 0.5-2 Sek |
| Denormalized (eine Tabelle) | 8-12 Sek | ~400-600 MB | 0.3-1 Sek |
| Snowflake (über-normalisiert) | 25-40 Sek | ~180-250 MB | 1-4 Sek |
Empfehlung: Star Schema ist der beste Kompromiss – akzeptable Ladezeit, optimaler Speicher, gute Performance. Für weitere Details siehe Star Schema in Qlik.
Welche Naming Conventions verbessern die Übersicht in Fact vs Dimension?
Klare Namen helfen Dir und Deinem Team sofort zu erkennen was was ist:
// Facts immer mit "Facts_" prefixen
Facts_Sales:
Facts_Inventory:
Facts_Budget:
// Dimensionen immer mit "Dim_" prefixen
Dim_Customer:
Dim_Product:
Dim_Calendar:
Vorteile:
- Im Datenmodell-Viewer sofort erkennbar was Facts und was Dims sind
- Beim Debuggen schneller navigieren
- Neue Team-Mitglieder verstehen die Struktur sofort
Checkliste: Ist Dein Design optimal?
Prüfe Dein Datenmodell mit diesen Fragen:
✓ Facts:
- [ ] Enthält nur Keys, Measures und Timestamps?
- [ ] Keine redundanten Attribute (Namen, Beschreibungen)?
- [ ] Granularität so detailliert wie für Analysen nötig?
- [ ] Measures sind numerisch und aggregierbar?
✓ Dimensionen:
- [ ] Enthält nur beschreibende Attribute?
- [ ] Keine Measures (Sum, Avg, Count)?
- [ ] Primary Key ist eindeutig (keine Duplikate)?
- [ ] Hierarchien sind denormalisiert (Region + Country + Continent in einer Tabelle)?
✓ Assoziationen:
- [ ] Facts und Dims verbinden sich über IDs?
- [ ] Keine Synthetic Keys?
- [ ] Keine Circular References?
Was sind die nächsten Schritte bei Fact vs Dimension – Design-Entscheidungen?
Du hast jetzt die Grundlagen für saubere Fact/Dimension-Designs. Als nächstes:
1. Many-to-Many Beziehungen: Wenn ein Kunde mehrere Adressen hat oder ein Produkt mehrere Kategorien – wie modellierst Du das? Link Tables für Many-to-Many zeigt Dir die Lösung.
2. Historisierung: Kunde war früher «Bronze» und ist jetzt «Gold» – wie behältst Du die Historie? Slowly Changing Dimensions erklärt wie’s geht.
3. Temporale Daten: Wie modellierst Du Gültigkeitszeiträume und Point-in-Time-Analysen? Temporale Daten & IntervalMatch ist der nächste Schritt.
Welche verwandten Themen gibt es im Kurs zu Fact vs Dimension?
- Star Schema in Qlik – Performance & Klarheit – Die Basis für Facts und Dimensionen
- Synthetic Keys & Circular References auflösen – Probleme vermeiden
- Link Tables für Many-to-Many Beziehungen – Komplexe Beziehungen modellieren
Slug: qlik-fact-dimension-design
Keywords: Qlik Sense Facts, Qlik Dimensions, Datenmodellierung Qlik, Star Schema Facts, Granularität Qlik, Fact Tables Qlik, Dimension Tables Qlik, Qlik Datenmodell Design, Qlik Performance Optimization, Qlik Best Practices