Guide data engineering

Dimensional Modeling Guide

Différence entre un data warehouse que les analystes adorent et un qu’ils évitent ? Le dimensional modeling. Apprenez à construire des star schemas intuitifs et rapides.

25 min de lecturePour analystes & data engineersExemples SQL inclus

1. Qu’est-ce que le dimensional modeling ?

Le dimensional modeling est une technique de design de data warehouse qui privilégie la simplicité des requêtes et la performance plutôt que l’efficacité de stockage. Créée par Ralph Kimball, elle reste la référence car elle modèle les données comme les métiers les conçoivent.

Pourquoi pas simplement du 3NF ?

La 3NF est parfaite pour l’OLTP : peu de redondance, pas d’anomalies de mise à jour. Pour l’analytics, c’est l’enfer : 15+ jointures par requête, performance en berne et schéma incompréhensible pour les analystes.

Principe clé : séparer le « ce qui s’est passé » du « contexte »

Tables de faits

Stockent des événements et mesures : commandes, clics, paiements. Contiennent des métriques agrégables (SUM, COUNT, AVG).

Tables de dimensions

Stockent le contexte descriptif : qui (clients), quoi (produits), quand (dates), où (sites). Permettent filtrage et groupement.

Philosophie Kimball

« Le data warehouse vaut ce que vaut la BI qu’il permet. » Les modèles dimensionnels sont conçus pour les humains d’abord. Si un analyste ne peut pas écrire de requête sans aide, le modèle a échoué.

2. Faits vs dimensions

Les briques fondamentales du dimensional modeling. Une fois maîtrisées, tout s’aligne.

Tables de faits

  • Enregistrent des événements/transactions
  • Contiennent des mesures numériques (montant, quantité, durée)
  • Ont des clés étrangères vers les dimensions
  • Haute et étroite (beaucoup de lignes, peu de colonnes)
  • Grain = une ligne par événement

Exemples :

fct_orders, fct_page_views, fct_payments

Tables de dimensions

  • Stockent des attributs descriptifs
  • Contiennent des champs texte pour filtrer/grouper
  • Ont une surrogate key en clé primaire
  • Basses et larges (peu de lignes, beaucoup de colonnes)
  • Grain = une ligne par entité

Exemples :

dim_customers, dim_products, dim_date

QuestionSi la réponse est...C’est une...
Peut-on faire SUM/COUNT/AVG dessus ?OuiFait
Voudriez-vous le GROUP BY ou FILTER ?OuiDimension
Décrit-il une entité ?OuiDimension
Enregistre-t-il un événement/transaction ?OuiFait

Exemple : commande e-commerce

-- Table de faits : une ligne par ligne de commande
CREATE TABLE fct_order_lines (
    order_line_sk       BIGINT PRIMARY KEY,    -- Surrogate key
    order_id            VARCHAR(50),           -- Natural key (degenerate dimension)
    customer_sk         BIGINT REFERENCES dim_customers,
    product_sk          BIGINT REFERENCES dim_products,
    date_sk             INT REFERENCES dim_date,
    -- Mesures (agrégables)
    quantity            INT,
    unit_price          DECIMAL(10,2),
    discount_amount     DECIMAL(10,2),
    line_total          DECIMAL(10,2)
);

-- Table de dimension : une ligne par client
CREATE TABLE dim_customers (
    customer_sk         BIGINT PRIMARY KEY,    -- Surrogate key
    customer_id         VARCHAR(50),           -- Natural key
    customer_name       VARCHAR(255),
    email               VARCHAR(255),
    segment             VARCHAR(50),           -- 'Enterprise', 'SMB', 'Consumer'
    acquisition_channel VARCHAR(50),
    created_at          TIMESTAMP
);

3. Design d’un star schema

Un star schema place une table de faits au centre, entourée de tables de dimensions. Le nom vient de la forme : le fait au milieu, les dimensions rayonnent comme les branches d’une étoile.

Structure Star Schema

dim_date

dim_customer

fct_orders

dim_product

dim_store

Table de faits au centre, dimensions autour — jointures simples, requêtes intuitives

Star vs Snowflake Schema

Star Schema (préféré)

  • • Dimensions dénormalisées
  • • Moins de jointures = requêtes plus rapides
  • • Plus facile à comprendre et interroger
  • • Légère redondance (acceptable pour l’analytics)

Snowflake Schema

  • • Dimensions normalisées
  • • Plus de jointures = requêtes plus lentes
  • • Difficile sans documentation
  • • Économie de stockage (rarement critique)

Astuce

Quand choisir le snowflake ?

Snowflake seulement si la dimension est vraiment hiérarchique ET si les analystes interrogent souvent des niveaux différents (produit → catégorie → département). Sinon, dénormalisez tout dans la dimension.

4. Types de dimensions

Toutes les dimensions ne se valent pas. Comprendre ces patterns vous aide à bien modéliser dès le départ.

1

Conformed dimensions

Partagées par plusieurs tables de faits. dim_date, dim_customer utilisées par fct_orders, fct_page_views, fct_support_tickets. Clé de la cohérence entreprise.

2

Role-playing dimensions

Même dimension, utilisée avec plusieurs rôles. dim_date jointe comme order_date, ship_date, delivery_date. Créez des vues ou alias.

3

Dimensions dégénérées

Clés de dimension stockées dans la table de faits sans dimension séparée. Numéro de commande, facture, transaction. Aucun attribut à stocker ailleurs.

4

Junk dimensions

Combine des flags à faible cardinalité dans une seule dimension. Au lieu de 5 booléens dans la table de faits, créez dim_order_flags avec toutes les combinaisons.

5

Dimension date

La dimension la plus importante. Attributs pré-calculés : day_of_week, is_weekend, fiscal_quarter, holiday_flag. Utilisez toujours des clés entières (format YYYYMMDD).

Exemple : dimension date

CREATE TABLE dim_date (
    date_sk             INT PRIMARY KEY,       -- Format YYYYMMDD
    date_actual         DATE NOT NULL,
    day_of_week         VARCHAR(10),           -- 'Monday', 'Tuesday', ...
    day_of_week_num     INT,                   -- 1-7
    day_of_month        INT,
    day_of_year         INT,
    week_of_year        INT,
    month_num           INT,
    month_name          VARCHAR(10),
    quarter_num         INT,
    quarter_name        VARCHAR(10),           -- 'Q1', 'Q2', ...
    year_num            INT,
    fiscal_year         INT,
    fiscal_quarter      INT,
    is_weekend          BOOLEAN,
    is_holiday          BOOLEAN,
    holiday_name        VARCHAR(50)
);

-- Usage : filtrer/grouper facilement par tout attribut date
SELECT 
    d.month_name,
    d.year_num,
    SUM(f.order_total) as revenue
FROM fct_orders f
JOIN dim_date d ON f.order_date_sk = d.date_sk
WHERE d.is_weekend = FALSE
GROUP BY d.month_name, d.year_num;

5. Slowly Changing Dimensions (SCD)

Les attributs de dimension évoluent. Un client déménage, un produit change de catégorie, un employé change de service. La façon dont vous gérez ces changements conditionne l’exactitude historique.

Type 0 : Fixe

Ne change jamais. La valeur initiale est conservée. À utiliser pour des attributs immuables (date d’inscription, date de naissance).

Exemple : Le canal d’acquisition initial reste même si le client revient par un autre canal.

Type 1 : Overwrite

L’ancienne valeur est écrasée. Pas d’historique. À utiliser pour les corrections ou attributs sans enjeu historique.

Compromis : Simple mais impossible de répondre à « quel était le segment lors de cette commande ? »

Type 2 : Nouvelle ligne (le plus fréquent)

Ajoute une nouvelle ligne de dimension avec une nouvelle surrogate key. Suivi via effective_date, expiry_date, is_current. Historique complet.

Idéal pour : tout attribut où l’historique compte. Segment client, adresse, niveau de prix.

Type 3 : Nouvelle colonne

Ajoute des colonnes current_value et previous_value. Historique limité (souvent une seule valeur précédente).

Rare : Utile seulement pour un avant/après précis. Le type 2 est presque toujours meilleur.

Exemple : SCD Type 2

-- SCD Type 2 : le client passe de 'SMB' à 'Enterprise'
-- AVANT : 1 ligne
customer_sk | customer_id | segment    | effective_date | expiry_date | is_current
1           | C001        | SMB        | 2023-01-01     | 9999-12-31  | TRUE

-- APRÈS : 2 lignes (ancienne expirée, nouvelle ajoutée)
customer_sk | customer_id | segment    | effective_date | expiry_date | is_current
1           | C001        | SMB        | 2023-01-01     | 2024-06-15  | FALSE
2           | C001        | Enterprise | 2024-06-15     | 9999-12-31  | TRUE

-- Requête historique : segment lors de la commande ?
SELECT 
    o.order_id,
    c.segment as segment_at_order_time
FROM fct_orders o
JOIN dim_customers c ON o.customer_sk = c.customer_sk
-- customer_sk dans la table de faits pointe vers la bonne version historique

Piège SCD Type 2

Avec le type 2, il faut décider au chargement quelle surrogate key attribuer au fait. En général, on veut la version « current » au moment de l’événement. Cela nécessite un lookup point-in-time pendant l’ETL.

6. Types de tables de faits

Les processus métier dictent le type de table de faits à utiliser. Choisissez selon la nature de l’événement.

Transaction facts

Une ligne par événement au grain le plus fin. Le plus courant. Commandes, clics, paiements, connexions.

Grain : Une ligne par ligne de commande

Periodic snapshot facts

Une ligne par période. Capture l’état à intervalles réguliers. Soldes de compte, stocks, snapshots pipeline.

Grain : Une ligne par compte et par jour

Accumulating snapshot facts

Une ligne par instance de processus, mise à jour au fil des jalons. Fulfillment de commande, demandes de prêt, tickets support.

Grain : Une ligne par commande (mise à jour sur le cycle de vie)

Factless facts

Enregistrent des événements sans mesure—juste des clés étrangères. Présence des étudiants, couverture promotionnelle produits.

Usage : Quels étudiants étaient présents ? Quels produits étaient en promo ?

Exemple : accumulating snapshot (fulfillment commande)

CREATE TABLE fct_order_fulfillment (
    order_sk                BIGINT PRIMARY KEY,
    order_id                VARCHAR(50),
    customer_sk             BIGINT,
    -- Plusieurs clés dates (jalons)
    order_date_sk           INT,
    payment_date_sk         INT,
    ship_date_sk            INT,
    delivery_date_sk        INT,
    -- Mesures de délai (calculées)
    days_to_payment         INT,
    days_to_ship            INT,
    days_to_delivery        INT,
    -- Mesures
    order_total             DECIMAL(10,2),
    current_status          VARCHAR(50)
);

-- La ligne est mise à jour au fur et à mesure
-- Au début : seulement order_date_sk
-- Après paiement : payment_date_sk rempli, days_to_payment calculé
-- Après expédition : ship_date_sk rempli, days_to_ship calculé
-- Après livraison : delivery_date_sk rempli, days_to_delivery calculé

7. Bonnes pratiques (checklist)

Définir le grain en premier

Avant toute table de faits, écrivez le grain : "Une ligne par ligne de commande" ou "Une ligne par client par jour". Ne mélangez jamais les grains.

Utiliser des surrogate keys

Clés entières sur toutes les dimensions. Les natural keys restent des attributs. Supporte correctement SCD Type 2 et améliore les jointures.

Construire des conformed dimensions

dim_date et dim_customer doivent être partagées par toutes les tables de faits. Clés et attributs identiques. Permet les analyses cross-process.

Dénormaliser les dimensions

Préférez le star au snowflake. Incluez catégorie, département, région directement dans la dimension. Le stockage est bon marché, les jointures sont chères.

Ajouter une dimension date

Ne joignez jamais sur des dates brutes. Créez dim_date avec attributs calculés. Utilisez des clés entières (YYYYMMDD) pour le partition pruning.

Gérer les nulls avec des lignes par défaut

Créez des lignes "Unknown" ou "Not Applicable" dans les dimensions (SK = -1). Pas de clés étrangères nulles dans les tables de faits.

Documenter le grain

Chaque table de faits doit documenter son grain. Évite le double comptage et aide les analystes à écrire des requêtes correctes.

SCD Type 2 pour les attributs clés

Segment client, catégorie produit, département—tout ce qui change et impacte l’analyse doit être en type 2.

Astuce

Le « test analyste »

Après le design, demandez à un analyste d’écrire 5 requêtes courantes sans documentation. Si chaque requête prend moins de 5 minutes, le modèle est bon. S’il faut poser des questions ou corriger, simplifiez.

8. Foire aux questions

Quelle est la différence entre table de faits et table de dimensions ?

Les tables de faits stockent des événements mesurables (transactions, clics, commandes) avec métriques et clés étrangères. Les dimensions stockent des attributs descriptifs (clients, produits, dates) pour donner du contexte. Les faits répondent « combien », les dimensions « qui, quoi, quand, où, pourquoi ».

Star schema vs snowflake schema ?

Le star schema relie des dimensions dénormalisées directement à une table de faits centrale, formant une étoile. Le snowflake normalise les dimensions en sous-dimensions (produit → catégorie → département). Le star est préféré car plus simple et plus performant.

Qu’est-ce qu’une Slowly Changing Dimension ?

Les SCD gèrent les changements d’attributs. Type 1 écrase, pas d’historique. Type 2 ajoute une ligne avec dates d’effet, historique complet. Type 3 ajoute des colonnes pour valeurs précédentes, historique limité. Le type 2 est le standard pour l’exactitude historique.

Surrogate keys ou natural keys ?

Utilisez des surrogate keys (entiers auto-générés) comme clés primaires des dimensions. Les natural keys (customer_id...) restent des attributs. Les surrogate keys sont stables, performantes et compatibles SCD Type 2. Les natural keys peuvent changer et casser les jointures.

Visualisez votre modèle dimensionnel

Créez des diagrammes star schema clairs montrant faits, dimensions et relations. Documentez le modèle pour que les analystes requêtent sans poser de questions.