Ein Product Manager möchte den Umsatz nach Kategorie und Region für das letzte Quartal, verglichen mit demselben Quartal des Vorjahres. In der Bestelldatenbank heißt das: fünf Tabellen joinen, einen Kalender spontan bauen und daran denken, dass Kunden zwischen Regionen umziehen. Jede neue Frage braucht neue Joins, und zwei Analysten kommen auf zwei verschiedene Zahlen.
Ein Sternschema löst das, indem es die Daten um einen Geschäftsprozess herum neu ordnet: in der Mitte eine Faktentabelle mit Messwerten, darum herum Dimensionstabellen, die sie beschreiben. Dieser Beitrag entwirft ein Verkaufs-Sternschema Schritt für Schritt, von der Granularität bis zum SQL, einschließlich langsam veränderlicher Dimensionen, Stern vs. Schneeflocke und spaltenorientierter Data Warehouses.
Was ein Sternschema ist
Ein Sternschema besteht aus zwei Arten von Tabellen:
- Faktentabellen erfassen Messwerte eines Geschäftsereignisses, etwa einer Bestellposition: Fremdschlüssel zu den Dimensionen plus numerische Kennzahlen. Sie sind schmal und lang.
- Dimensionstabellen enthalten den Kontext, das Wer, Was, Wo und Wann: breite, flache, kurze Tabellen mit Textattributen wie Kategorie oder Region.
Die Kimball Group, die viele dieser Techniken benannt hat, beschreibt es als Faktentabellen, die über Primär- und Fremdschlüssel mit Dimensionstabellen verknüpft sind. Microsofts Power-BI-Leitfaden fasst den Reiz einfach zusammen: Dimensionstabellen ermöglichen Filtern und Gruppieren, Faktentabellen ermöglichen Aggregieren.
In vier Schritten entwerfen
Kimballs vierstufiger dimensionaler Designprozess besteht aus vier Entscheidungen, in dieser Reihenfolge:
- Den Geschäftsprozess wählen: Einzelhandelsumsatz, in Filialen und im Webshop.
- Die Granularität festlegen: eine Zeile pro Bestellposition, also ein Produkt in einer Bestellung. Kimball nennt die Granularität einen verbindlichen Vertrag, der vor jeder Dimension und jedem Fakt festgelegt wird.
- Die Dimensionen bestimmen: Datum, Produkt, Filiale, Kunde und Aktion, jeweils mit einem Wert pro Bestellposition.
- Die Fakten bestimmen: Menge, Stückpreis, Rabatt, Nettobetrag und Kosten, alle gültig bei dieser Granularität.
Beginne bei der atomaren Granularität, der niedrigsten Ebene, die die Quelle erfasst; Zusammenfassungen entstehen aus atomaren Zeilen, nie umgekehrt. Mische nie Granularitäten: Versandkosten, die pro Bestellung berechnet, aber in jeder Zeile gespeichert werden, werden pro Zeile einmal gezählt. Verteile sie mit einer Geschäftsregel auf die Zeilen, wie Kimball rät, oder gib ihnen eine eigene Faktentabelle.
Die Beispieltabellen
| Spalte in fact_sales | Rolle | Hinweise |
|---|---|---|
date_key |
Fremdschlüssel | Sprechender Schlüssel wie 20261120 |
product_key, store_key, customer_key, promotion_key |
Fremdschlüssel | Surrogat-Ganzzahlen |
order_number |
Degenerierte Dimension | Keine Dimensionstabelle dahinter |
line_number |
Zeilenidentität | Eindeutig zusammen mit order_number |
quantity, discount_amount, net_amount, cost_amount |
Additive Kennzahlen | Über jede Dimension summierbar |
unit_price |
Nicht additive Kennzahl | Niemals summieren |
Die Dimensionen sind bewusst flach: dim_product trägt Marke, Unterkategorie und Kategorie in einer Zeile, und dim_store behandelt den Webshop als eine weitere Filiale mit channel = 'online'. order_number ist eine degenerierte Dimension: Der Kontext des Kopfes liegt in anderen Dimensionen, es bleibt nur die Nummer, die trotzdem nützlich ist, um Warenkörbe zu gruppieren.
Der Stückpreis ist die klassische Falle: Über Zeilen summiert ist er bedeutungslos, teile stattdessen additive Teile, wie in SUM(net_amount) / SUM(quantity).
Surrogatschlüssel und die Datumsdimension
Jede Dimension bekommt einen Surrogatschlüssel, eine bedeutungslose Ganzzahl, die fortlaufend ab 1 vergeben wird, denn eine Dimension mit Historie hat mehrere Zeilen pro natürlichem Schlüssel, und Quellen sind sich bei Schlüsseln oft uneins. Behalte den natürlichen Schlüssel (sku, store_code, customer_id) als Attribut, um Zeilen zur Quelle zurückzuverfolgen.
Die Datumsdimension ist die Ausnahme: stabil und vorhersehbar, daher kann ihr Schlüssel sprechend sein, etwa 20261120, was auch bei der Partitionierung hilft. Filtere und gruppiere über ihre Attribute (year, quarter, is_holiday), nicht über Arithmetik auf dem Schlüssel. Zwanzig Jahre sind nur etwa 7.305 Zeilen (20 × 365,25).
Fakten-Fremdschlüssel sollten nie null sein: Ein Null-Schlüssel lässt die Zeile aus Inner Joins fallen, und Summen schrumpfen unbemerkt. Nutze Standardzeilen in den Dimensionen für unbekannte oder nicht zutreffende Werte; hier bedeutet promotion_key 0 «Keine Aktion».
Langsam veränderliche Dimensionen
Der Typ der langsam veränderlichen Dimension (SCD) entscheidet, was Berichte zeigen, nachdem sich ein Attribut geändert hat:
| Typ | Name | Bei einer Änderung | Historie |
|---|---|---|---|
| 0 | Original behalten | Der Wert ändert sich nie | Nur der ursprüngliche Wert |
| 1 | Überschreiben | Zeile an Ort und Stelle aktualisieren | Keine |
| 2 | Neue Zeile anlegen | Aktuelle Zeile schließen, neue mit neuem Schlüssel einfügen | Vollständig |
| 3 | Neues Attribut anlegen | Alten Wert in eine prior_-Spalte kopieren, überschreiben |
Ein früherer Wert |
Kimballs Typen 4 bis 7 ergänzen Mini-Dimensionen und Hybride aus diesen.
Typ 2 ist das Arbeitspferd. Kunde C-1042 zieht am 20. November 2026 von Austin nach Denver:
| customer_key | customer_id | city | valid_from | valid_to | is_current |
|---|---|---|---|---|---|
| 1001 | C-1042 | Austin | 2024-03-14 | 2026-11-20 | false |
| 2417 | C-1042 | Denver | 2026-11-20 | 9999-12-31 | true |
Frühere Verkäufe behalten Schlüssel 1001 und bleiben in Austin; spätere Verkäufe bekommen 2417. Der Fakten-Load wählt die Zeile, deren Bereich das Bestelldatum enthält (valid_from <= order_date < valid_to). Kimballs Definition von Typ 2 verlangt mindestens ein Gültigkeitsdatum, ein Ablaufdatum und einen Indikator für die aktuelle Zeile; ein weit in der Zukunft liegendes valid_to statt null hält Bereichsfilter einfach. dbt-Snapshots setzen Typ 2 für dich um.
Um die gesamte Historie nach der aktuellen Stadt auszuwerten, joine auf die aktuelle Zeile mit derselben customer_id. Change Data Capture speist Typ 2 gut, weil jede Aktualisierung als Ereignis ankommt; siehe das Change-Data-Capture-Diagramm.
Sternschema vs. Schneeflockenschema vs. One Big Table
| Sternschema | Schneeflockenschema | One Big Table | |
|---|---|---|---|
| Dimensionsdaten | Eine flache Tabelle pro Dimension | Normalisierte Ketten: Produkt, Unterkategorie, Kategorie | Auf jede Faktenzeile kopiert |
| Joins pro Abfrage | Einer pro verwendeter Dimension | Einer pro Tabelle in jeder Kette | Keine |
| Ein Attribut korrigieren | Eine Dimensionstabelle aktualisieren | Eine Tabelle der Kette aktualisieren | Jede betroffene Faktenzeile neu schreiben |
| Am besten für | Die meisten Data Marts und BI-Modelle | Für Analytik selten lohnend | Ein Dashboard oder ML-Extrakt, aus einem Stern erzeugt |
Kimball rät von Schneeflocken-Dimensionen ab: Sie sind für Fachanwender schwerer zu durchblicken, können die Abfrageleistung verschlechtern, und eine abgeflachte Dimension enthält exakt dieselben Informationen. Der Power-BI-Leitfaden sieht das genauso.
One Big Table braucht keine Joins, und spaltenorientierte Kompression hält wiederholte Werte günstig. Der Preis ist die Wartung: Korrekturen werden zu Neuschreibungen und neue Attribute zu Neuaufbauten, also erzeuge sie aus dem Stern.
Eine Abfrage, die das Modell einfach macht
SELECT d.year_month,
p.category,
SUM(f.net_amount) AS revenue,
SUM(f.net_amount - f.cost_amount) AS gross_margin,
SUM(f.net_amount) / NULLIF(SUM(f.quantity), 0) AS avg_selling_price
FROM fact_sales AS f
JOIN dim_date AS d ON d.date_key = f.date_key
JOIN dim_product AS p ON p.product_key = f.product_key
JOIN dim_store AS s ON s.store_key = f.store_key
WHERE d.year = 2026
AND s.channel = 'online'
GROUP BY d.year_month, p.category
ORDER BY d.year_month, revenue DESC;
Jeder Join geht vom Fakt zu einer Dimension über einen Integer-Schlüssel; Filter liegen auf Dimensionsattributen und Summen auf Faktenspalten. Eine neue Frage ändert WHERE und GROUP BY, nicht die Joins, weshalb BI-Tools solche Abfragen zuverlässig erzeugen.
Performance in spaltenorientierten Warehouses
Spaltenorientierte Warehouses lesen nur die Spalten, die eine Abfrage nutzt, was zu einer schmalen Faktentabelle passt, und kleine Dimensionen lassen sich günstig joinen: Bei einem Broadcast Join schickt BigQuery die kleinere Tabelle an jeden Worker. Mit INT64-Schlüsseln und Mengen zu 8 logischen Bytes und NUMERIC-Beträgen zu 16 liest die obige Abfrage 64 Bytes pro Bestellposition aus dem Fakt, etwa 32 GB für 500 Millionen Zeilen, egal wie breit die Tabelle ist.
- Nach Datum beschneiden. Partitioniere, clustere oder sortiere den Fakt nach
date_key. BigQuery beschneidet Partitionen, wenn auf die Partitionsspalte selbst ein konstanter Filter gesetzt ist; große Scans sollten also auchdate_keymit Konstanten filtern, nicht nurdim_date.year. - Schlüssel deklarieren, auch ohne Durchsetzung. BigQuery nutzt Primär- und Fremdschlüssel, die als
NOT ENFORCEDdeklariert sind, um Joins zu eliminieren und umzusortieren, und Snowflake eliminiert Joins bei Constraints mitRELY. Beide prüfen die Daten nicht, und verletzte Constraints können falsche Ergebnisse liefern, also teste Schlüssel in der Pipeline. - In Redshift gemeinsam ablegen. Redshifts Leitfaden empfiehlt, Fakt und eine Dimension nach ihrem gemeinsamen Schlüssel zu verteilen, für einige andere Dimensionen die ALL-Verteilung zu nutzen oder
AUTOwählen zu lassen.
Sternschemata liegen meist in der Gold-Schicht einer Medallion-Architektur; das Datenpipeline-Architekturdiagramm zeigt, wie die Daten dorthin kommen, und ETL vs. ELT, wo Transformationen laufen.
Das Verkaufs-Sternschema-Diagramm
Diagramminhalt
- fact_sales
- dim_date
- dim_customer
- dim_product
- dim_promotion
- dim_store
- date_key
- customer_key
- promotion_key
- product_key
- store_key
So liest du es:
- fact_sales in der Mitte enthält fünf Fremdschlüssel, die degenerierte
order_number,line_numberund fünf Kennzahlen. - Krähenfüße an jeder Linie: null oder viele Faktenzeilen auf der einen Seite, genau eine Dimensionszeile auf der anderen. Die Beschriftungen nennen die Join-Schlüssel.
- Türkise Karten sind Dimensionen, mit dim_date oben und den übrigen links und rechts.
- Die bernsteinfarbene Karte, dim_customer, hält die Historie als SCD Typ 2.
- Der Markdown-Block nennt die Granularität und welche Kennzahlen du summieren darfst.
Sternschema-Diagramme klar zeichnen
- Schreibe die Granularität ins Diagramm. Reviewer fragen zuerst danach, und eine falsch gelesene Granularität führt zu Doppelzählungen.
- Setze den Fakt in die Mitte und jede Dimension einen Schritt entfernt. Eine Linie zwischen zwei Dimensionen ist eine Schneeflocke; zeichne sie bewusst oder flache sie ab.
- Markiere Schlüsselrollen: PK, FK, NK für natürliche Schlüssel und DD für degenerierte Dimensionen.
- Zeige den SCD-Typ bei Dimensionen, die Historie halten.
- Lass weg: Load-Jobs, Staging-Tabellen und Spalten, nach denen niemand filtert.
- Zeichne einen Geschäftsprozess pro Diagramm. Wenn Faktentabellen konforme Dimensionen teilen, ergänze eine Bus-Matrix: Prozesse als Zeilen, Dimensionen als Spalten.
Zur Krähenfuß-Notation siehe das ER-Diagramm-Beispiel.
In Archboard nachbauen
- Melde dich mit Google oder GitHub an und erstelle ein Diagramm.
- Wenn die Tabellen existieren, füge ihre
CREATE TABLE-Anweisungen in das SQL-zu-ER-Diagramm-Tool ein, oder DBML in das DBML-zu-ER-Diagramm-Tool, für einen ersten Entwurf. - Füge aus dem Paket Entity-Relationship eine Entität namens fact_sales hinzu, ein Attribut pro Zeile: fünf mit FK markierte Schlüssel, order_number mit DD markiert, line_number, dann die Kennzahlen. Gib ihr einen blauen Akzent.
- Füge fünf weitere Entität-Nodes hinzu: dim_date über dem Fakt, dim_customer und dim_promotion links, dim_product und dim_store rechts. Markiere Surrogatschlüssel mit PK und natürliche Schlüssel mit NK; nutze türkise Akzente, bernsteinfarben für dim_customer.
- Verbinde fact_sales mit jeder Dimension per Krähenfuß, null oder viele am Fakt und genau eins an der Dimension, und beschrifte jede Linie mit ihrem Schlüssel.
- Füge Text-Notizen aus dem Paket Basis hinzu: eine Schlüssellegende, die SCD-Typ-2-Regel, den sprechenden Datumsschlüssel, die Standardzeile für Aktionen und den Webshop.
- Füge unter dem Fakt einen Markdown-Block aus dem Paket Basis mit der Granularität und den Additivitätsregeln hinzu.
Das Beispiel nutzt 7 Diagramm-Nodes und bleibt damit im Free-Plan.
FAQ
Was ist ein Sternschema in einfachen Worten? Eine zentrale Faktentabelle mit Messwerten, etwa Umsatzbeträgen, verknüpft mit Dimensionstabellen, die sie beschreiben, etwa Datum, Produkt und Kunde. Gezeichnet sieht es aus wie ein Stern.
Was ist der Unterschied zwischen einer Faktentabelle und einer Dimensionstabelle? Eine Faktentabelle speichert Messwerte von Ereignissen in einer festgelegten Granularität plus Fremdschlüssel. Eine Dimensionstabelle speichert die Attribute, nach denen du filterst und gruppierst, etwa Kategorie oder Region.
Was ist der Unterschied zwischen einem Sternschema und einem Schneeflockenschema? Ein Stern hält jede Dimension in einer flachen Tabelle; eine Schneeflocke normalisiert sie in Ketten wie Produkt, Unterkategorie und Kategorie. Sterne brauchen weniger Joins und sind einfacher zu nutzen.
Ist ein Sternschema normalisiert? Nicht vollständig. Dimensionstabellen sind bewusst denormalisiert und wiederholen Werte wie Kategorienamen in jeder Produktzeile, während die Faktentabelle schmal bleibt.
Sind Sternschemata in spaltenorientierten Warehouses noch wichtig? Ja. Breite Tabellen lassen sich in spaltenorientierten Engines günstig scannen, aber ein Stern gibt dir weiterhin eine einzige Stelle, um ein Attribut zu korrigieren, Historie über langsam veränderliche Dimensionen und konsistente Antworten über Berichte hinweg.