Schéma en étoile, de bout en bout

Résumé

Un schéma en étoile met au centre une table de faits (les mesures d’un événement) entourée de dimensions (le contexte : date, produit, client). On le construit en quatre décisions prises dans l’ordre : le processus métier, le grain, les dimensions, les faits. Cette note construit un petit schéma complet, exécuté sur DuckDB, depuis les tables de la note de normalisation jusqu’aux requêtes d’analyse.

Prérequis

Normalisation 2NF et 3NF (le point de départ transactionnel) et Inmon, Kimball et Data Vault (l’approche dimensionnelle). Le vocabulaire est dans le Glossaire data engineering.

Les quatre décisions

La méthode de Kimball se résume en quatre décisions, prises avec le métier en tenant compte des réalités des données sources :1

  1. Choisir le processus métier : ici, la prise de commande.
  2. Déclarer le grain : ce que représente exactement une ligne de la table de faits.
  3. Identifier les dimensions : le contexte descriptif.
  4. Identifier les faits : les mesures numériques.

Le grain d’abord

C’est la décision qui conditionne les autres : dire ce que représente une ligne de la table de faits, et s’y tenir. Toute dimension ou mesure envisagée doit être compatible avec ce choix. Kimball recommande de partir du grain atomique, le niveau le plus détaillé que le processus enregistre, car il répond aussi aux questions qu’on n’avait pas prévues ; un grain agrégé accélère des requêtes connues mais suppose qu’on sait déjà quelles questions seront posées. Deux grains différents appellent deux tables de faits.2

Ici : une ligne par ligne de commande (une commande, un produit).

Le schéma

erDiagram
    FCT_LIGNE_COMMANDE }o--|| DIM_DATE : "date_key"
    FCT_LIGNE_COMMANDE }o--|| DIM_PRODUIT : "produit_key"
    FCT_LIGNE_COMMANDE }o--|| DIM_CLIENT : "client_key"

Au grain le plus fin, une ligne de faits correspond à un événement mesuré dans le monde réel, ce qui rend sa conception indépendante des rapports qu’on en tirera. Elle porte les mesures numériques, une clé étrangère par dimension, et parfois une dimension dégénérée (ici le numéro de commande) ou des horodatages. C’est elle que les requêtes agrègent.3

Les dimensions

create table dim_date (
  date_key int primary key,       -- 20261005 : clé « intelligente », exception usuelle pour la date
  date_jour date, annee int, mois int, nom_mois text);
 
create table dim_produit (
  produit_key int primary key,    -- clé de substitution
  produit_id text,                -- clé naturelle de la source
  nom text, categorie text);
 
create table dim_client (
  client_key int primary key,
  client_id int, nom text, ville text);

Chaque dimension a une clé de substitution (*_key), distincte de la clé de la source : Kimball recommande que chaque jointure entre faits et dimensions repose sur ces clés, qui généralisent la clé naturelle et protègent des changements de la source.4 La dimension dim_client est celle où un type de SCD s’applique : Slowly Changing Dimensions, types 0 à 3.

La dimension de date se génère plutôt qu’elle ne se charge. Sur DuckDB :

insert into dim_date
select cast(strftime(d, '%Y%m%d') as int), d, year(d), month(d), strftime(d, '%Y-%m')
from (select unnest(generate_series(date '2026-01-01', date '2026-03-31', interval 1 day))::date as d);

(Sur BigQuery, UNNEST(GENERATE_DATE_ARRAY(DATE '2026-01-01', DATE '2026-03-31')) joue ce rôle.) Elle porte tous les attributs de calendrier utiles aux analyses (année, mois, jour de semaine, jour férié) pour ne pas les recalculer dans chaque requête.

Les faits

create table fct_ligne_commande (
  commande_id int,            -- dimension dégénérée
  ligne int,
  date_key int, produit_key int, client_key int,
  quantite int,               -- fait additif
  montant decimal(10,2),      -- fait additif
  primary key (commande_id, ligne));

Les mesures quantite et montant sont additives : on peut les sommer sur n’importe quelle dimension. Un prix unitaire, lui, ne se somme pas : le stocker comme fait demande de réfléchir à son agrégation. Le prix payé est conservé dans la ligne de fait, pas lu depuis la dimension produit, pour garder l’historique des ventes (voir l’avertissement de Normalisation 2NF et 3NF).

Les données d’exemple

commande_idlignedate_keyproduitclientquantitémontant
10120260105ClavierDupont (Lyon)250,00
10220260105SourisDupont (Lyon)115,00
11120260210ÉcranDupont (Lyon)1250,00
12120260120ClavierMartin (Toulouse)5125,00
13120260202SourisDurand (Inconnue)230,00

Le client Durand n’avait pas de ville dans la source : la dimension porte la valeur « Inconnue » plutôt qu’un NULL, pour que les regroupements affichent une ligne lisible.

Interroger l’étoile

Toute requête suit le même patron : joindre les faits aux dimensions utiles, filtrer et regrouper sur leurs attributs, agréger les mesures.

select d.nom_mois, p.categorie, sum(f.montant) as ca, sum(f.quantite) as qte
from fct_ligne_commande f
join dim_date d using (date_key)
join dim_produit p using (produit_key)
group by d.nom_mois, p.categorie
order by 1, 2;
nom_moiscategoriecaqte
2026-01Périphériques190,008
2026-02Affichage250,001
2026-02Périphériques30,002
select cl.ville, sum(f.montant) as ca
from fct_ligne_commande f join dim_client cl using (client_key)
group by 1 order by 2 desc;
villeca
Lyon315,00
Toulouse125,00
Inconnue30,00

Changer d’axe d’analyse (par ville plutôt que par catégorie) ne demande que de changer la dimension jointe ; la table de faits ne change pas.

Contrôles

Deux requêtes à garder dans les tests du modèle (voir Observabilité des données) :

-- faits orphelins : une clé qui ne pointe vers aucune dimension
select count(*) from fct_ligne_commande f
left join dim_produit p using (produit_key) where p.produit_key is null;
 
-- respect du grain : une seule ligne par (commande, ligne)
select commande_id, ligne, count(*) from fct_ligne_commande
group by 1, 2 having count(*) > 1;

Sur les données d’exemple, la première renvoie 0 et la seconde aucune ligne. Sur BigQuery, les clés primaires et étrangères déclarées ne sont pas appliquées : ces requêtes (ou des tests dbt unique et relationships) sont le seul contrôle réel.

Choix et limites

  • Plus de jointures qu’une table plate : c’est le prix de la clarté ; les moteurs colonnaires gèrent bien ces jointures (OLTP vs OLAP et stockage en colonnes).
  • Schéma en flocon : normaliser une dimension en plusieurs tables économise de la place mais complique les requêtes ; c’est une technique décrite par Kimball, souvent déconseillée en pratique pour cette raison.
  • Plusieurs processus, plusieurs étoiles : chaque processus a sa table de faits ; elles partagent des dimensions conformées.
  • Une étoile construite dans dbt : les dimensions et faits sont des modèles dim_ et fct_ de la couche marts : dbt — Fondamentaux.

Notes liées

Notes de bas de page

  1. Kimball Group, Four-Step Dimensional Design Process, consulté le 24 octobre 2026. ↩

  2. Kimball Group, Grain, consulté le 24 octobre 2026. ↩

  3. Kimball Group, Fact Table Structure, consulté le 24 octobre 2026. ↩

  4. Ralph Kimball, Surrogate Keys (design tip, 2 mai 1998), consulté le 24 octobre 2026. ↩